Alien-Base-ModuleBuild

 view release on metacpan or  search on metacpan

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

# alien_build_commands: arrayref of commands for installing the library
__PACKAGE__->add_property(
  alien_install_commands =>
  default => [ 'make install' ],
);

# alien_version_check: command to execute to check if install/version
__PACKAGE__->add_property( alien_version_check => '%{pkg_config} --modversion %n' );

# pkgconfig-esque info, author provides these by hand for now, will parse .pc file eventually
__PACKAGE__->add_property( 'alien_provides_cflags' );
__PACKAGE__->add_property( 'alien_provides_libs' );

# alien_repository: hash (or arrayref of hashes) of information about source repo on ftp
#   |-- protocol: ftp or http
#   |-- protocol_class: holder for class type (defaults to 'Net::FTP' or 'HTTP::Tiny')
#   |-- host: ftp server for source
#   |-- location: ftp folder containing source, http addr to page with links
#   |-- pattern: qr regex matching acceptable files, if has capture group those are version numbers
#   |-- platform: src or platform, matching os_type M::B method
#   |

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

__PACKAGE__->add_property( 'alien_extra_site_config' => {} );

################
#  ConfigData  #
################

# working_directory: full path to the extracted source or binary of the library
# pkgconfig: hashref of A::B::PkgConfig objects created from .pc file found in working_directory
# install_type: either system or share
# version: version number installed or available
# Cflags: holder for cflags if manually specified
# Libs:   same but libs
# name: holder for name as needed by pkg-config
# finished_installing: set to true once ACTION_install is finished, this helps testing now and real checks later

############################
#  Initialization Methods  #
############################

sub new {
  my $class = shift;

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

  $self->config_data( ffi_name => $self->alien_ffi_name );

  my $version;
  $version = $self->alien_check_installed_version
    unless $self->config_data('Force');

  if ($version) {
    $self->config_data( install_type => 'system' );
    $self->config_data( version => $version );
    my %system_provides;
    $system_provides{Cflags} = $self->alien_provides_cflags
      if defined $self->alien_provides_cflags;
    $system_provides{Libs}   = $self->alien_provides_libs
      if defined $self->alien_provides_libs;
    $self->config_data( system_provides => \%system_provides );
    return;
  }

  if ($self->config_data('ForceSystem')) {
    die "Requested system install, but system package not detected."
  }

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

    # remove anything already in PATH
    delete $path{$_} for grep { defined $_ } @PATH;
    # add anything else to start of PATH
    unshift @PATH, sort keys %path;

    $config ||= _shell_config_generate();
    $config->prepend_path( PATH => sort keys %path );
  }

  # If we are using autoconf, then create a site.config file
  # that specifies the same compiler and compiler linker flags
  # as were used for building Perl.  This is helpful for
  # mixed 32/64bit platforms where 32bit is the default but
  # Perl is 64bit.
  if($self->config_data('autoconf') && !defined $ENV{CONFIG_SITE}) {

    local $CWD;
    pop @CWD;

    my $ldflags = $Config{ldflags};
    $ldflags .= " -Wl,-headerpad_max_install_names"
      if $^O eq 'darwin';

    my %extra_site_config = %{ $self->alien_extra_site_config };
    open my $fh, '>', 'config.site';
    print $fh "CC='$Config{cc}'\n";
    # -D define flags should be stripped because they are Perl
    # specific.
    print $fh "CFLAGS='"   . join(" ", _filter_defines($Config{ccflags}), (delete($extra_site_config{CFLAGS}) ||"")) . "'\n";
    print $fh "CPPFLAGS='" . join(" ", _filter_defines($Config{cppflags}), (delete($extra_site_config{CPPFLAGS}) ||"")) . "'\n";
    print $fh "CXXFLAGS='" . join(" ", _filter_defines($Config{ccflags}), (delete($extra_site_config{CXXFLAGS}) ||"")) . "'\n";
    print $fh "LDFLAGS='"  . join(" ", $ldflags, (delete($extra_site_config{LDFLAGS}) ||"") ) . "'\n";
    for (keys %extra_site_config) {
        print $fh "$_='$extra_site_config{$_}'\n";
    }
    close $fh;

    my $config ||= _shell_config_generate();
    my $config_site = File::Spec->catfile($CWD, 'config.site');
    $config_site =~ s{\\}{/}g if $^O eq 'MSWin32';
    $config->set( CONFIG_SITE => $config_site );
    $ENV{CONFIG_SITE} = $config_site;

lib/Alien/Base/ModuleBuild.pm  view on Meta::CPAN

    $provides_libs = join( ' ', @files );
  }

  my $libs = join( ' ', @L, $provides_libs );

  my @I =
    map { "-I$_" }
    map { _catdir( '${pcfiledir}', $_ ) }
    @{$paths->{inc}};

  my $provides_cflags = $self->alien_provides_cflags;
  push @I, $provides_cflags if $provides_cflags;
  my $cflags = join( ' ', @I );

  my $manual_pc = Alien::Base::PkgConfig->new({
    package  => $self->alien_name,
    vars     => {
      pcfiledir => $dir,
    },
    keywords => {
      Cflags  => $cflags || '',
      Libs    => $libs || '',
      Version => '',
    },
  });

  return $manual_pc;
}

sub _alien_file_pattern_dynamic {
  my $self = shift;

lib/Alien/Base/ModuleBuild/API.pod  view on Meta::CPAN

[version 0.006]

On windows wrap build and install commands in an C<MSYS> environment using L<Alien::MSYS>.  This option will automatically add L<Alien::MSYS> as a build requirement when building on Windows.

=item alien_name

[version 0.001]

The name of the primary library which will be provided. This should be in the form to be passed to C<pkg-config>. This name is available in the L<command interpolation|/"COMMAND INTERPOLATION"> as C<%n>.

=item alien_provides_cflags

=item alien_provides_libs

[version 0.001]

These parameters, if specified, augment the information found by F<pkg-config>. If no package config data is found, these are used to generate the necessary information. In that case, if these are not specified, they are attempted to be created from ...

=item alien_repository

[version 0.001]

lib/Alien/Base/ModuleBuild/Authoring.pod  view on Meta::CPAN

 $builder->create_build_script;

This is just like you would do for L<Module::Build>, except that there will be a few additional configuration parameters (see L<Alien::Base::ModuleBuild::API>).

L<Alien::Base::ModuleBuild> adds the additional build actions C<alien_code> and C<alien_install>. These actions need never be run directly, the usual C<build> action (usually seen as C<./Build>) and C<install> (C<./Build install>) will call them for ...

The C<./Build test> command will invoke any library tests specified in C<alien_test_commands>, though none are defined by default. Finally C<./Build install> will invoke whatever C<alien_install_commands> were specified.

=head2 Alien::Base

L<Alien::Base> is the base class of C<Alien::MyLibrary>. In this context, L<Alien::Base> has two distinct uses. First it is used by C<Alien::MyLibrary> to provide the build information/flags for building C<Some::Module::MyLibrary>. Secondly it is use...

=head3 Alien::Base for Building

C<Alien::MyLibrary> is called by C<Some::Library::MyLibrary>'s build script, either F<Build.PL> or F<Makefile.PL>. Most of the functionality can be utilized through class method calls, though creating an object can save a few keystrokes.

 # file: Some-Module-MyLibrary/Build.PL
 use Module::Build;
 use Alien::MyLibrary;
 
 my $alien = Alien::MyLibrary->new;
 my $builder = Module::Build->new(
   ...
   extra_compiler_flags => $alien->cflags(),
   extra_linker_flags   => $alien->libs(),
 );
 $builder->create_build_script;

Additional information can be gotten from the C<config> method.

=head3 Alien::Base for Run-Time Provision

C<Alien::MyLibrary> must be a subclass of L<Alien::Base>. This provides the C<import> method, which does the run-time provisioning so that when the XS file is loaded, it can find F<libmylibrary.so>. The C<import> method does this by pre-loading the l...

 # file: Alien-MyLibrary/lib/Alien/MyLibrary.pm

lib/Alien/Base/ModuleBuild/FAQ.pod  view on Meta::CPAN

to CPAN.  For a manifesto style description of the Why, and How see L<Alien>.  L<Alien::Base> is a base
class and framework for creating Alien distributions.  The idea is to address as many of the common challenges
to developing Alien modules in the base class to simplify the process.

=head2 How do I specify a minimum or exact version requirement for packages that use pkg-config?

The C<alien_version_check> attribute to L<Alien::Base::ModuleBuild> will be executed to determine if
the library is provided by the operating system.  The default for this is C<%{pkg_config} --modversion %n>
which simply checks to see if any version of that package is available, and prints the version
number.  You can use the C<--atleast-version>, C<--exact-version> options to require a specific range of versions,
but these flags do not work with the C<--modversion> flag, so be sure to invoke separately.

 use Alien::Base::ModuleBuild;
 Alien::Base::ModuleBuild->new(
   dist_name           => 'Alien::Foo',
   alien_name          => 'foo',
   configure_requires  => { 'Alien::Base::ModuleBuild' => '0.022' }, # required for %{pkg_config}
   alien_version_check => '%{pkg_config} --atleast-version 1.2.3 %n && %{pkg_config} --modversion %n',
   ...
 )->create_build_script;

It is better to use the built in C<%{pkg_config}> helper as it will use the system provided pkg-config
if it is available and fallback on the pure perl L<PkgConfig> if not.

You can also use C<--exact-version> to specify an exact version.

=head2 How to create an Alien module for packages that do not support pkg-config?

Although L<Alien::Base> and L<Alien::Base::ModuleBuild> assume packages come with a C<pkg-config>
C<.pc> file to determine compiler and linker flags by default, you can implement an Alien module
for packages that do use C<pkg-config> by following these tasks:

=over 4

=item subclass L<Alien::Base::ModuleBuild> and implement C<alien_check_installed_version>

Create a subclass of L<Alien::Base::ModuleBuild> and put it in the C<inc> directory of your distribution so
that it can be used during install but won't I<be installed>.

 # inc/My/ModuleBuild.pm

lib/Alien/Base/ModuleBuild/FAQ.pod  view on Meta::CPAN

   # otherwise return the version detected
   # (if you cannot determine the version it
   #  is usually sufficient to return a true value)
   return $version;
 }

There are number of methods you can use to determine if the system provides your library.  From Perl
methods include L<Devel::CheckLib>, L<ExtUtils::CBuilder>, L<ExtUtils::CChecker>, L<Config::AutoConf>,
L<FFI::CheckLib> among others.  It is also frequently possible to determine if a library is installed
using a C<-config> suffixed program.  For example C<libxml2> comes with xml2-config which provides the
existence, compiler and linker flags it needs.  In my experience, however, most packages that provide a
C<-config> suffixed program also provide a C<pkg-config> interface as well.

=item implement C<alien_check_built_version> in your L<Alien::Base::ModuleBuild> subclass

You should also implement C<alien_check_build_version> which will be executed from the package build
root once the package is successfully built.

 # inc/My/ModuleBuild.pm
 package My::ModuleBuild;
 

lib/Alien/Base/ModuleBuild/FAQ.pod  view on Meta::CPAN

 sub alien_check_built_version {
   my($self) = @_;
 
   my $version = ...
 
   # (Again, if you cannot determine the version,
   #  it is usually sufficent to return a true value)
   return $version;
 }

=item set C<alien_provides_cflags> and C<alien_provides_libs> in C<Build.PL>.

Add something like this to your C<Build.PL>:

 # Build.PL
 use lib 'inc';
 use My::ModuleBuild;
 
 My::ModuleBuild->new(
   ...
   alien_provides_cflags => '-I/usr/include/foo',
   alien_provides_libs   => '-L/usr/lib/foo -lfoo',
   ...
 );

Note that it is frequently sufficient to provide C<alien_provides_libs> and the appropriate C<-l> flag.
These flags will be used in the event that the system package can be found.  It is a good idea to verify
that these flags do indeed work in C<alien_check_installed_version> above.

=back

For a fully implemented example, see L<Alien::Libbz2>.

=head2 How do I test my package once it is built (before it is installed)?

There are many ways to test Alien modules before (or after) they are installed, but instead
of rolling your own, consider using L<Test::Alien> which is light on dependencies and will
test your module very closely to the way that it will actually be used.  That is to say by

lib/Alien/Base/ModuleBuild/FAQ.pod  view on Meta::CPAN

     '%c --prefix=%s',
     'make',
   ],
   alien_install_commands => [
     'make install',
   ],
   ...
 )->create_build_script;

There are a couple of short cuts here, C<%c> indicates the platform independent method for executing the
C<configure> script, plus any normal autoconf flags that are appropriate for Perl Alien libraries. The C<%c>
also tells L<Alien::Base::ModuleBuild> to use L<Alien::MSYS> on Windows platforms and to add that as a
dependency.  The C<%s> is a placeholder for the location to which the package will be installed.  This is
normally in a share directory specific to your distribution.

=head3 autoconf-like

If you see an error like this:

 Unknown option "--with-pic".

t/alien_base_modulebuild.t  view on Meta::CPAN


    sub alien_check_installed_version {
      return '1.0';
    }

    __PACKAGE__;
  };

  subtest 'not installed, not forced' => sub {
    local $Alien::Base::ModuleBuild::Force = 0;
    my $builder = builder( alien_provides_cflags => '-DMY_CFLAGS', alien_provides_libs => '-L/my/libs -lmylib' );
    $builder->depends_on('code');
    is $builder->config_data('system_provides')->{Cflags}, '-DMY_CFLAGS',          'cflags';
    is $builder->config_data('system_provides')->{Libs},   '-L/my/libs -lmylib', 'libs';
  };
};

subtest 'alien_env' => sub {

  local $CWD = _new_temp();
  local $ENV{BAZ} = 'baz';

  my $builder = builder(



( run in 4.653 seconds using v1.01-cache-2.11-cpan-94b05bcf43c )