package ActivePerl::PPM::PPD;
use strict;
use ActivePerl::PPM::ParsePPD ();
use ActivePerl::PPM::Package ();
use ActivePerl::PPM::Logger qw(ppm_log);
use URI ();
sub ActivePerl::PPM::Package::new_ppd {
my($class, $pkg, %opt) = @_;
my $arch = delete $opt{arch} || "noarch";
my $base = delete $opt{base};
my $rel_base = delete $opt{rel_base};
unless (ref $pkg) {
my $data = $pkg;
$pkg = undef;
my $p = ActivePerl::PPM::ParsePPD->new(sub {
$pkg = shift;
});
eval {
$p->parse_more($data);
$p->parse_done;
};
if ($@) {
# malformed XML
ppm_log("ERR", $@);
return undef;
}
unless ($pkg) {
ppm_log("ERR", "No SOFTPKG found in parsed PPD document");
return undef;
}
}
if (exists $pkg->{codebase}) {
my $pkg_arch = $pkg->{architecture} || "noarch";
unless ($arch eq $pkg_arch || $pkg_arch eq "noarch") {
delete $pkg->{codebase};
delete $pkg->{script};
}
}
if (!exists $pkg->{codebase}) {
# Move relevant attributes for the matching implementation up
for my $impl (@{$pkg->{implementation} || []}) {
my $impl_arch = $impl->{architecture} || "noarch";
if ($arch eq $impl_arch || $impl_arch eq "noarch") {
for my $k (keys %$impl) {
if (ref($impl->{$k}) eq "HASH") {
for my $k2 (keys %{$impl->{$k}}) {
$pkg->{$k}{$k2} = $impl->{$k}{$k2};
}
}
else {
$pkg->{$k} = $impl->{$k};
}
}
}
}
}
delete $pkg->{implementation}; # not used any more
# rebase URIs
$base = "dummy:/" unless $base;
if ($pkg->{base}) {
$base = URI->new_abs($pkg->{base}, $base);
$pkg->{base} = $base->as_string;
$pkg->{base} =~ s,^dummy:/,,;
}
if ($base ne "dummy:/" || $rel_base) {
my @uri_ref;
if (exists $pkg->{codebase}) {
push(@uri_ref, \$pkg->{codebase});
}
if (exists $pkg->{script}) {
for my $kind (keys %{$pkg->{script}}) {
next unless exists $pkg->{script}{$kind}{uri};
push(@uri_ref, \$pkg->{script}{$kind}{uri});
}
}
for my $uri_ref (@uri_ref) {
my $uri = URI->new_abs($$uri_ref, $base);
$uri = $uri->rel($rel_base) if $rel_base;
$uri = $uri->as_string;
$uri =~ s,^dummy:/,,;
$$uri_ref = $uri;
}
}
# convert legacy OSD version number
for my $version ($pkg->{version}) {
if ($version =~ /^\d+(?:,\d+){3}/) {
$version =~ s/,/./g;
1 while $version =~ s/(\d\.\d+)\.0+$/$1/; # drop trailing '.0's
}
}
$pkg->{arch} = $arch;
return $class->new($pkg);
}
1;
__END__
=head1 NAME
ActivePerl::PPM::PPD - Parser for PPD files
=head1 SYNOPSIS
my $ppd = ActivePerl::PPM::Package->new_ppd('...');
=head1 DESCRIPTION
This module adds the C constructor to the
C class. This constructor parses PPD
files and allow package objects to be initialized from these
files. PPD is an XML based format that is used to describe PPM
packages.
The following methods are added:
=over
=item $ppd = ActivePerl::PPM::Package->new_ppd( $ppd_document, %opt )
=item $ppd = ActivePerl::PPM::Package->new_ppd( $parsed_ppd_hashref, %opt )
The constructor take a literal document as argument and will return
and object representing the PPD. The method returns C if
$ppd_document does not contain the expected XML.
The following options are supported:
=over
=item arch => $archname
The $archname should be specified to select attributes for a specific
architecture where the PPD describes multiple implementations. The
value C is the default and will only select
implementation sections without any ARCHITECTURE restriction.
=item base => $base_uri
All URIs in the PPD will be made absolute with $base_uri as base.
=item rel_base => $base_uri
All URIs in the PPD will be made relative if they can be resolved from
$base_uri. Only safe to use together with C which is applied
first. If both C and C are the same, they cancel
each other out and the effect is the same as if none of them where
specified.
=back
=back
=head1 PPD XML FORMAT
The PPM PPD is an XML based format used to describe PPM packages.
The format is based on the now defunct OSD specification
(L). This shows an example of a
minimal PPD document:
Leon Brocard (leon@astray.com)
An encoding scheme for Buffy the Vampire Slayer fans
The following elements are used:
=over
=item ...
Content is a short statement describing the purpose of this
package. No attributes. Parent must be a SOFTPKG element.
=item
The required attribute NAME should match
C<$Config{archname}-$major_vers> for the perl this package was
compiled for. If this element is missing, it's the same as
specifying . No content. Parent must be
either SOFTPKG or IMPLEMENTATION.
Packages or implementations marked with "noarch" are assumed to
installable on any architecture.
=item ...
Content is the package author's name (with email address). No
attributes. Parent must be a SOFTPKG element.
=item
The required HREF attribute provides a URI where the binary package
(the tared up C tree) of the package can be obtained. The URI
can be relative and is then resolved based on the URI of the PPD
document. No content. Parent must be SOFTPKG or
IMPLEMENTATION.
=item
Deprecated. Required attribute is NAME. Optional attribute is
VERSION. No content. Element might be repeated any number of
times. Parent must be an IMPLEMENTATION element.
This element expresses a dependency on another package with the given
name and with the given version number or better. The other package
must be installed for this package to work.
This element is still recommended for PPDs that are to be used by
both PPM4 and PPM3 clients, as the PPM3 clients will ignore any
REQUIRE elements provided. PPM4 clients regard DEPENDENCY the same as
REQUIRE, but will simply ignore the VERSION provided.
=item ...
No attributes. Optional container for ARCHITECTURE, DEPENDENCY,
INSTALL, PROVIDE, REQUIRE, UNINSTALL elements. Parent must be
SOFTPKG. There can be multiple instances of IMPLEMENTATION but they
should each contain an ARCHITECTURE element that differ from each
other.
=item
=item ...
Optional attributes are EXEC and HREF. Textual content can be included.
Provides a script or commands to run after the C files of the package
have been installed, a so called post-install script. The script to
run can either be provided inline or externally via HREF. If both
are provided then only the HREF is used. Parent must be either SOFTPKG or
IMPLEMENTATION.
If EXEC is provided, it gives the name of the interpreter to run the
script. For historical reasons, if the script is not obtained via HREF
then any occurences of double semicolon ";;" is replaced by newline
before it is saved in a temporary file and passed as the first argument to
the EXEC interpreter. The special value "PPM_PERL" ensures that the
script runs with the same perl interpreter that runs PPM. The special
value "SELF" make the script run as a self contained executable.
If EXEC is not provided, the commands of the script are passed to
the system command interpreter (via C) one by one. If the
script was obtained via HREF, each line is considered a command.
If the script was obtained from the content, then a double semicolon
";;" is used to separate commands.
When the script/command runs it uses the unpacked package tarball
(obtained by downloading the CODEREF) as the working directory, and
the following environment variables will be set:
=over
=item PPM_ACTION
One of "install", "upgrade" or "uninstall".
=item PPM_INSTARCHLIB
The archlib directory of the current install area.
=item PPM_INSTLIB
The lib directory of the current install area.
=item PPM_INSTPACKLIST
The name of the installed F<.packlist> file of the package.
=item PPM_INSTROOT
The prefix directory of the current install area.
=item PPM_NEW_VERSION
The version label of the package just installed.
=item PPM_PERL
The path to the perl that runs PPM.
=item PPM_PREV_VERSION
The version label that the package had before the upgrade started.
This variable is only present when PPM_ACTION is "upgrade".
=item PPM_VERSION
The version of PPM currently running.
=back
=item
Required attribute is NAME. Optional attribute is VERSION. No
content. Element can be repeated any number of times. Parent must
be either SOFTPKG or IMPLEMENTATION.
The NAME represents a feature that this package provides if installed.
Any label goes. VERSION is a floating point number.
Packages containing perl modules should have one PROVIDE element for
each module installed by the package. Module names that do not
naturally contain double colon "::" should have "::" appended to them.
=item ...
Element must be root if present. Container for a set of SOFTPKG
elements. Optional attributes are ARCHITECTURE and BASE.
ARCHITECTECTURE provides the default for all contained SOFTPKG elements
that do not have an explicit ARCHITECTECTURE element.
BASE overrides the base URI that the relative URIs of CODEBASE, INSTALL
and UNINSTALL are resolved from. If BASE itself is relative, it is
first resolved based on the URI of the PPD document.
The file name F is commonly used for documents
containing a REPOSITORY root.
=item ...
Treated the same as REPOSITORY. Supported for backwards compatibility
with old style F files.
=item
Required attribute is NAME. Optional attribute is VERSION. No
content. Element might be repeated any number of times. Parent must
be either SOFTPKG or IMPLEMENTATION.
This element expresses a dependency on some other package that provides
the feature given by NAME and with the given version number or better.
A package that provides the given feature must be installed for this
package to work.
=item ...
Represents a package available for PPM to install. Container for all
the other elements defined here (except REPOSITORY and
REPOSITORYSUMMARY).
Required attributes are NAME and VERSION. Optional attribute is DATE.
The NAME and VERSION value can be any label. Older versions of this
specification had a more strict definition of VERSION as a sequence of
exactly 4 numbers in the range 0 to 65535 separated by comma. If such
values are encountered then they are converted to "standard" format by
replacing the commas with dots and trimming off ".0.0" or ".0".
The DATE attribute should use ISO 8601 formatted date (or datetime)
stamps. That is "YYYY-MM-DD" or "YYYY-MM-DDThh:mm:ssZ" format. See
L for more information.
Parent must be REPOSITORY or REPOSITORYSUMMARY, or the SOFTPKG can be
the document root. Content elements can be in any order.
Documents where SOFTPKG is root are normally stored in files with the
F<.ppd> extension.
=item
=item ...
Used for scripts that run just before the C files of the package
is uninstalled. The attributes and content are treated the same as for
INSTALL and the same set of environment variables are availabe to the
script.
The uninstall script runs with a new, clean temporary directory
as its working directory. The directory and its content is removed after
the script finishes.
=back
=head2 Changes since PPM3
The PPD format has changed in PPM4. This section lists the differences:
=over
=item *
The format of the SOFTPKG/VERSION attribute has been relaxed. This
attribute should now contain the version identifier as used for the
original package. PPM will not be able to order packages based on
this label.
=item *
The SOFTPKG/DATE attribute has been introduced. This should be the
release date of the package. For CPAN packages this should be the
date when the package was uploaded to CPAN.
=item *
Added REQUIRE and PROVIDE elements that are used to describe
features that this package depends on and provides. The NAME
attribute is required for both. The VERSION attribute is optional and
should be a floating number. Features are assumed to be backwards
compatible and a feature with a higher version number is regarded
as better.
=item *
The DEPENDENCY elements are deprecated. Use REQUIRE instead. If
present they are mapped to REQUIRE but their VERSION attribute is
ignored.
=item *
The OS, OSVERSION, PROCESSOR, PERLCORE elements are deprecated and
always ignored. Implementations are matched using the ARCHITECTURE
element only.
=item *
The TITLE element is deprecated and ignored. The SOFTPKG/NAME
attribute is the title.
=back
=head1 SEE ALSO
L, L