Alien-Build
view release on metacpan or search on metacpan
lib/Alien/Build.pm view on Meta::CPAN
# TODO: this is probably a bug that we don't set
# download or compelte properties?
$self->log("single dir, assuming directory");
}
else
{
$self->log("single file, assuming archive");
}
$self->install_prop->{download} = $archive->absolute->stringify;
$self->install_prop->{complete}->{download} = 1;
$valid = 1;
}
else
{
$self->log("multiple files, assuming directory");
$self->install_prop->{complete}->{download} = 1;
$self->install_prop->{download} = _path('.')->absolute->stringify;
$valid = 1;
}
},
after => sub {
$CWD = $self->root;
},
},
'download',
);
# experimental and undocumented for now
if($self->meta->has_hook('check_download'))
{
$self->meta->call_hook(check_download => $self);
}
return $self if $valid;
}
else
{
# This will call the default download hook
# defined in Core::Download since the recipe
# does not provide a download hook
my $ret = $self->_call_hook('download');
# experimental and undocumented for now
if($self->meta->has_hook('check_download'))
{
$self->meta->call_hook(check_download => $self);
}
return $self;
}
die "download failed";
}
sub fetch
{
my $self = shift;
my $url = $_[0] || $self->meta_prop->{start_url};
my $secure = 0;
if(defined $url && ($url =~ /^(https|file):/ || $url !~ /:/))
{
# considered secure when either https or a local file
$secure = 1;
}
elsif(!defined $url)
{
$self->log("warning: undefined url in fetch");
}
else
{
$self->log("warning: attempting to fetch a non-TLS or bundled URL: @{[ $url ]}");
}
die "insecure fetch is not allowed" if $self->download_rule =~ /^(encrypt|digest_and_encrypt)$/ && !$secure;
my $file = $self->_call_hook( 'fetch' => @_ );
$secure = 0;
if(ref($file) ne 'HASH')
{
$self->log("warning: fetch returned non-hash reference");
}
elsif(!defined $file->{protocol})
{
$self->log("warning: fetch did not return a protocol");
}
elsif($file->{protocol} !~ /^(https|file)$/)
{
$self->log("warning: fetch did not use a secure protocol: @{[ $file->{protocol} ]}");
}
else
{
$secure = 1;
}
die "insecure fetch is not allowed" if $self->download_rule =~ /^(encrypt|digest_and_encrypt)$/ && !$secure;
$file;
}
sub check_digest
{
my($self, $file) = @_;
return '' unless $self->meta_prop->{check_digest};
unless(ref($file) eq 'HASH')
{
my $path = Path::Tiny->new($file);
$file = {
type => 'file',
filename => $path->basename,
path => "$path",
tmp => 0,
};
}
my $path = $file->{path};
if(defined $path)
{
# there is technically a race condition here
die "Missing file in digest check: @{[ $file->{filename} ]}" unless -f $path;
die "Unreadable file in digest check: @{[ $file->{filename} ]}" unless -r $path;
}
else
{
die "File is wrong type" unless defined $file->{type} && $file->{type} eq 'file';
die "File has no filename" unless defined $file->{filename};
die "@{[ $file->{filename} ]} has no content" unless defined $file->{content};
}
my $filename = $file->{filename};
my $signature = $self->meta_prop->{digest}->{$filename} || $self->meta_prop->{digest}->{'*'};
die "No digest for $filename" unless defined $signature && ref $signature eq 'ARRAY';
my($algo, $expected) = @$signature;
if($self->meta->call_hook( check_digest => $self, $file, $algo, $expected ))
{
# record the verification here so that we can check in the extract step that the signature
# was checked.
$self->install_prop->{download_detail}->{$path}->{digest} = [$algo, $expected] if defined $path; return 1;
}
else
{
die "No plugin provides digest algorithm for $algo";
}
}
sub decode
{
my($self, $res) = @_;
my $res2 = $self->_call_hook( decode => $res );
$res2->{protocol} = $res->{protocol}
if !defined $res2->{protocol}
&& defined $res->{protocol};
return $res2;
}
sub prefer
{
my($self, $res) = @_;
my $res2 = $self->_call_hook( prefer => $res );
$res2->{protocol} = $res->{protocol}
if !defined $res2->{protocol}
&& defined $res->{protocol};
return $res2;
}
sub extract
{
my($self, $archive) = @_;
$archive ||= $self->install_prop->{download};
unless(defined $archive)
{
die "tried to call extract before download";
}
{
my $checked_digest = 0;
my $encrypted_fetch = 0;
my $detail = $self->install_prop->{download_detail}->{$archive};
if(defined $detail)
{
if(defined $detail->{digest})
{
my($algo, $expected) = @{ $detail->{digest} };
my $file = {
type => 'file',
filename => Path::Tiny->new($archive)->basename,
path => $archive,
tmp => 0,
};
$checked_digest = $self->meta->call_hook( check_digest => $self, $file, $algo, $expected )
}
if(!defined $detail->{protocol})
{
$self->log("warning: extract did not receive protocol details for $archive") unless $checked_digest;
}
elsif($detail->{protocol} !~ /^(https|file)$/)
{
$self->log("warning: extracting from a file that was fetched via insecure protocol @{[ $detail->{protocol} ]}") unless $checked_digest ;
}
else
{
$encrypted_fetch = 1;
}
}
else
{
$self->log("warning: extract received no download details for $archive");
}
if($self->download_rule eq 'digest')
{
die "required digest missing for $archive" unless $checked_digest;
}
elsif($self->download_rule eq 'encrypt')
{
die "file was fetched insecurely for $archive" unless $encrypted_fetch;
}
elsif($self->download_rule eq 'digest_or_encrypt')
{
die "file was fetched insecurely and required digest missing for $archive" unless $checked_digest || $encrypted_fetch;
}
elsif($self->download_rule eq 'digest_and_encrypt')
{
die "file was fetched insecurely and required digest missing for $archive" unless $checked_digest || $encrypted_fetch;
die "required digest missing for $archive" unless $checked_digest;
die "file was fetched insecurely for $archive" unless $encrypted_fetch;
}
elsif($self->download_rule eq 'warn')
{
unless($checked_digest || $encrypted_fetch)
{
$self->log("!!! NOTICE OF FUTURE CHANGE IN BEHAVIOR !!!");
$self->log("a future version of Alien::Build will die here by default with this exception: file was fetched insecurely and required digest missing for $archive");
$self->log("!!! NOTICE OF FUTURE CHANGE IN BEHAVIOR !!!");
}
}
else
{
die "internal error, unknown download rule: @{[ $self->download_rule ]}";
}
}
my $nick_name = 'build';
if($self->meta_prop->{out_of_source})
{
$nick_name = 'extract';
my $extract = $self->install_prop->{extract};
return $extract if defined $extract && -d $extract;
}
my $tmp;
local $CWD;
my $ret;
$self->_call_hook({
before => sub {
# called build instead of extract, because this
# will be used for the build step, and technically
# extract is a substage of build anyway.
$tmp = Alien::Build::TempDir->new($self, $nick_name);
$CWD = "$tmp";
},
verify => sub {
my $path = '.';
if($self->meta_prop->{out_of_source} && $self->install_prop->{extract})
{
$path = $self->install_prop->{extract};
}
my @list = grep { $_->basename !~ /^\./ && $_->basename ne 'pax_global_header' } _path($path)->children;
my $count = scalar @list;
if($count == 0)
{
die "no files extracted";
}
elsif($count == 1 && -d $list[0])
{
$ret = $list[0]->absolute->stringify;
}
else
{
$ret = "$tmp";
}
},
after => sub {
$CWD = $self->root;
lib/Alien/Build.pm view on Meta::CPAN
probe [ 'some command %{.meta.plugin_fetch_newprotocol_foo}' ];
probe [ 'some command %{.install.plugin_fetch_newprotocol_bar}' ];
probe [ 'some command %{.runtime.plugin_fetch_newprotocol_baz}' ];
probe [ 'some command %{.meta.my_foo}' ];
probe [ 'some command %{.install.my_bar}' ];
probe [ 'some command %{.runtime.my_baz}' ];
=head2 meta_prop
my $href = $build->meta_prop;
my $href = Alien::Build->meta_prop;
Meta properties have to do with the recipe itself, and not any particular
instance that probes or builds that recipe. Meta properties can be changed
from within an L<alienfile> using the C<meta_prop> directive, or from
a plugin from its C<init> method (though should NOT be modified from any
hooks registered within that C<init> method). This is not strictly enforced,
but if you do not follow this rule your recipe will likely be broken.
=over
=item arch
This is a hint to an installer like L<Alien::Build::MM> or L<Alien::Build::MB>,
that the library or tool contains architecture dependent files and so should
be stored in an architecture dependent location. If not specified by your
L<alienfile> then it will be set to true.
=item check_digest
True if cryptographic digest should be checked when files are fetched
or downloaded. This is set by
L<Digest negotiator plugin|Alien::Build::Plugin::Digest::Negotiate>.
=item destdir
Some plugins (L<Alien::Build::Plugin::Build::Autoconf> for example) support
installing via C<DESTDIR>. They will set this property to true if they
plan on doing such an install. This helps L<Alien::Build> find the staged
install files and how to locate them.
If available, C<DESTDIR> is used to stage install files in a sub directory before
copying the files into C<blib>. This is generally preferred method
if available.
=item destdir_filter
Regular expression for the files that should be copied from the C<DESTDIR>
into the stage directory. If not defined, then all files will be copied.
=item destdir_ffi_filter
Same as C<destdir_filter> except applies to C<build_ffi> instead of C<build>.
=item digest
This properties contains the cryptographic digests (if any) that should
be used when verifying any fetched and downloaded files. It is a hash
reference where the key is the filename and the value is an array
reference containing a pair of values, the first being the algorithm
('SHA256' is recommended) and the second is the actual digest. The
special filename C<*> may be specified to indicate that any downloaded
file should match that digest. If there are both real filenames and
the C<*> placeholder, the real filenames will be used for filenames
that match and any other files will use the placeholder. Example:
$build->meta_prop->{digest} = {
'foo-1.00.tar.gz' => [ SHA256 => '9feac593aa49a44eb837de52513a57736457f1ea70078346c60f0bfc5f24f2c1' ],
'foo-1.01.tar.gz' => [ SHA256 => '6bbde6a7f10ae5924cf74afc26ff5b7bc4b4f9dfd85c6b534c51bd254697b9e7' ],
'*' => [ SHA256 => '33a20aae3df6ecfbe812b48082926d55391be4a57d858d35753ab1334b9fddb3' ],
};
Cryptographic signatures will only be checked
if the L<check_digest meta property|/check_digest> is set and if the
L<Digest negotiator plugin|Alien::Build::Plugin::Digest::Negotiate> is loaded.
(The Digest negotiator can be used directly, but is also loaded automatically
if you use the L<digest directive|alienfile/digest> is used by the L<alienfile>).
=item env
Environment variables to override during the build stage.
=item env_interpolate
Environment variable values will be interpolated with helpers. Example:
meta->prop->{env_interpolate} = 1;
meta->prop->{env}->{PERL} = '%{perl}';
=item local_source
Set to true if source code package is available locally. (that is not fetched
over the internet). This is computed by default based on the C<start_url>
property. Can be set by an L<alienfile> or plugin.
=item platform
Hash reference. Contains information about the platform beyond just C<$^O>.
=over 4
=item platform.compiler_type
Refers to the type of flags that the compiler accepts. May be expanded in the
future, but for now, will be one of:
=over 4
=item microsoft
On Windows when using Microsoft Visual C++
=item unix
Virtually everything else, including gcc on windows.
=back
The main difference is that with Visual C++ C<-LIBPATH> should be used instead
of C<-L>, and static libraries should have the C<.LIB> suffix instead of C<.a>.
lib/Alien/Build.pm view on Meta::CPAN
plugin instance id that made the probe. If the probe results in a
system install this will be propagated to C<system_probe_instance_id>
for later use.
=back
=head1 METHODS
=head2 checkpoint
$build->checkpoint;
Save any install or runtime properties so that they can be reloaded on
a subsequent run in a separate process. This is useful if your build
needs to be done in multiple stages from a C<Makefile>, such as with
L<ExtUtils::MakeMaker>. Once checkpointed you can use the C<resume>
constructor (documented above) to resume the probe/build/install]
process.
=head2 root
my $dir = $build->root;
This is just a shortcut for:
my $root = $build->install_prop->{root};
Except that it will be created if it does not already exist.
=head2 install_type
my $type = $build->install_type;
This will return the install type. (See the like named install property
above for details). This method will call C<probe> if it has not already
been called.
=head2 is_system_install
my $boolean = $build->is_system_install;
Returns true if the alien is a system install type.
=head2 is_share_install
my $boolean = $build->is_share_install;
Returns true if the alien is a share install type.
=head2 download_rule
my $rule = $build->download_rule;
This returns install rule as a string. This is determined by the environment
and should be one of:
=over 4
=item C<warn>
Warn only if fetching via non secure source (secure sources include C<https>,
and bundled files, may include other encrypted protocols in the future).
=item C<digest>
Require that any downloaded source package have a cryptographic signature in
the L<alienfile> and that signature matches what was downloaded.
=item C<encrypt>
Require that any downloaded source package is fetched via secure source.
=item C<digest_or_encrypt>
Require that any downloaded source package is B<either> fetched via a secure source
B<or> has a cryptographic signature in the L<alienfile> and that signature matches
what was downloaded.
=item C<digest_and_encrypt>
Require that any downloaded source package is B<both> fetched via a secure source
B<and> has a cryptographic signature in the L<alienfile> and that signature matches
what was downloaded.
=back
The current default is C<warn>, but in the near future this will be upgraded to
C<digest_or_encrypt>.
=head2 set_prefix
$build->set_prefix($prefix);
Set the final (unstaged) prefix. This is normally only called by L<Alien::Build::MM>
and similar modules. It is not intended for use from plugins or from an L<alienfile>.
=head2 set_stage
$build->set_stage($dir);
Sets the stage directory. This is normally only called by L<Alien::Build::MM>
and similar modules. It is not intended for use from plugins or from an L<alienfile>.
=head2 requires
my $hash = $build->requires($phase);
Returns a hash reference of the modules required for the given phase. Phases
include:
=over 4
=item configure
These modules must already be available when the L<alienfile> is read.
=item any
These modules are used during either a C<system> or C<share> install.
=item share
These modules are used during the build phase of a C<share> install.
=item system
These modules are used during the build phase of a C<system> install.
=back
=head2 load_requires
$build->load_requires($phase);
This loads the appropriate modules for the given phase (see C<requires> above
for a description of the phases).
=head2 probe
my $install_type = $build->probe;
( run in 0.834 second using v1.01-cache-2.11-cpan-39bf76dae61 )