The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

$Dist::Build::XS::VERSION = '0.019';
use strict;
use File::Basename qw/basename dirname/;
use File::Spec::Functions qw/catfile curdir/;
use Text::ParseWords 'shellwords';
sub get_flags {
my ($raw) = @_;
return $raw if not defined($raw) or ref($raw);
return [ shellwords($raw) ]
}
sub add_methods {
my ($self, $planner, %args) = @_;
$planner->add_delegate('add_xs', sub {
my ($planner, %args) = @_;
my $pureperl_only = $args{pureperl_only} // $planner->pureperl_only;
die "Can't build xs files under --pureperl-only\n" if $pureperl_only;
$planner = $planner->new_scope;
my $config = $args{config} // $planner->config;
$planner->load_extension('ExtUtils::Builder::ParseXS', 0.016, config => $config) unless $planner->can('parse_xs');
$planner->load_extension('ExtUtils::Builder::AutoDetect::C', 0.016, config => $config) unless $planner->can('compile');
my $xs_base = $args{xs_base} // 'lib';
my ($module_name, $xs_file);
if (defined $args{module}) {
$module_name = $args{module};
$xs_file = $args{file} // catfile($xs_base, split /::/, $module_name) . '.xs';
} elsif (defined $args{file}) {
$xs_file = $args{file};
$module_name = $planner->module_for_xs($xs_file, $xs_base);
} else {
$module_name = $planner->main_module;
$xs_file = catfile($xs_base, split /::/, $module_name) . '.xs';
}
my $module_version = $args{version} // $planner->version;
my $xs_dir = dirname($xs_file);
my $c_file = $planner->c_file_for_xs($xs_file, $xs_dir);
$planner->parse_xs($xs_file, $c_file, %args, module => $module_name);
my $o_file = $planner->obj_file(basename($c_file, '.c'), $xs_dir);
my %defines = (
%{ $args{defines} // {} },
VERSION => qq/"$module_version"/,
XS_VERSION => qq/"$module_version"/,
);
my $compiler_flags = get_flags($args{extra_compiler_flags});
$planner->compile($c_file, $o_file,
type => 'loadable-object',
profile => '@Perl',
defines => \%defines,
include_dirs => [ dirname($xs_file), @{ $args{include_dirs} // [] } ],
extra_args => $compiler_flags,
config => $config,
);
my @objects = $o_file;
for my $source (@{ $args{extra_sources} }) {
my %options = ref $source ? %{ $source } : (source => $source);
my $dirname = dirname($options{source});
my $object = $options{object} // $planner->obj_file(basename($options{source}, '.c'), $dirname);
my %defines = (%{ $args{defines} // {} }, %{ $options{defines} // {} });
my @include_dirs = (@{ $args{include_dirs} // [] }, @{ $options{include_dirs} // [] });
my @compiler_flags = (@{ $compiler_flags // [] }, @{ $options{flags} // [] });
$planner->compile($options{source}, $object,
type => 'loadable-object',
profile => '@Perl',
defines => \%defines,
include_dirs => \@include_dirs,
extra_args => \@compiler_flags,
config => $config,
);
push @objects, $object;
}
push @objects, @{ $args{extra_objects} } if $args{extra_objects};
my $lib_file = $planner->extension_filename($module_name);
$planner->link(\@objects, $lib_file,
type => 'loadable-object',
profile => '@Perl',
module_name => $module_name,
mkdir => 1,
extra_args => get_flags($args{extra_linker_flags}),
library_dirs => $args{library_dirs},
libraries => $args{libraries},
config => $config,
);
$planner->create_phony('dynamic', $lib_file);
return $lib_file;
});
$planner->add_delegate('auto_xs', sub {
my ($planner, %args) = @_;
my $dir = delete $args{dir} // 'lib';
my $xs_files = $planner->create_pattern(
dir => $dir,
file => '*.xs'
);
my $so_files = $planner->create_subst(
on => $xs_files,
subst => sub {
my ($source) = @_;
$planner->add_xs(
%args,
xs_base => $dir,
module => undef,
file => $source,
);
},
);
return;
});
}
1;
# ABSTRACT: An XS implementation for Dist::Build
__END__
=pod
=encoding UTF-8
=head1 NAME
Dist::Build::XS - An XS implementation for Dist::Build
=head1 VERSION
version 0.019
=head1 SYNOPSIS
# planner/xs.pl
load_extension('Dist::Build::XS');
add_xs(
module => 'Foo::Bar',
extra_sources => [ glob 'src/*.c' ],
libraries => [ 'foo' ],
);
=head1 DESCRIPTION
This module implements support for XS for Dist::Build.
=head1 METHODS
=head2 add_xs
This method takes the following named arguments, all optional:
=over 4
=item * module
The name of the module to be compiled. This defaults to C<$main_module> unless C<file> is given, in which case the name is derived from the path.
=item * version
The version of the module, defaulting to the dist version.
=item * file
The name of the XS file. By default it's derived from the C<$module_name>, e.g. C<lib/Foo/Bar.xs> for C<Foo::Bar>.
=item * defines
This hash contains defines for the C files. E.g. C<< { DEBUG => 1 } >>.
=item * include_dirs
A list of directories to add to the include path. For the xs file the directory it is in is automatically added to this list.
=item * extra_sources
A list of C files to compile with this module. Instead of just a name, entries can also be a hash with the following entries:
=over 4
=item * source
The name of the input file. Mandatory.
=item * object
The name of the object that will be compiled. Will be derive from the source name by default.
=item * include_dirs
An array containing additional include directories for this objects
=item * defines
A hash containing additional defines for this object.
=item * flags
An array containing additional flags for this compilation.
=back
=item * extra_objects
A list of object files to link with the module.
=item * extra_compiler_flags
Additional flags to feed to the compiler. This can either be an array or a (shell-quoted) string.
=item * extra_sources
Extra C files to compile with this module.
=item * library_dirs
Extra libraries to find libraries in.
=item * libraries
Libraries to link to.
=item * extra_linker_flags
Additional flags to feed to the compiler. This can either be an array or a (shell-quoted) string.
=back
=head2 auto_xs
This method is like C<add_xs>, except that instead of taking C<module> or C<file> named arguments, it takes a C<dir> argument (defaulting to C<'lib'>). It will search that directory for all XS files, and build them with the other arguments passed to this function.
=head1 EXTENSIONS
Various extensions exist that modify the behavior of C<add_xs>. Among these are:
=over 4
=item * L<Dist::Build::XS::Import|Dist::Build::XS::Import>
This adds an C<import> argument to imports include directories and compilation flags exported by other modules using L<Dist::Build::XS::Export|Dist::Build::XS::Export>.
=item * L<Dist::Build::XS::WriteConstants|Dist::Build::XS::WriteConstants>
This adds a C<write_constants> argument, integrating L<ExtUtils::Constant|ExtUtils::Constant>.
=item * L<Dist::Build::XS::Alien|Dist::Build::XS::Alien>
This adds an C<alien> argument to link to libraries using L<Alien::Base|Alien::Base>.
=item * L<Dist::Build::XS::PkgConfig|Dist::Build::XS::PkgConfig>
This adds a C<pkg_config> argument to link to libraries using C<pkg-config> files.
=back
=head1 AUTHOR
Leon Timmermans <fawaka@gmail.com>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2024 by Leon Timmermans.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut