Affix
view release on metacpan or search on metacpan
builder/Affix/Builder.pm view on Meta::CPAN
use Config qw[%Config];
field $force : param //= 0;
field $debug : param = 0;
field $libver;
field $cflags;
field $ldflags;
field $cppver = 'c++17'; # https://en.wikipedia.org/wiki/C%2B%2B20#Compiler_support
field $cver = 'c17'; # https://en.wikipedia.org/wiki/C17_(C_standard_revision)
field $make : param //= $Config{make};
#
field $action : param //= 'build';
field $meta : reader = CPAN::Meta->load_file('META.json');
# Params to Build script
field $install_base : param //= '';
field $installdirs : param //= '';
field $uninst : param //= 0; # Make more sense to have a ./Build uninstall command but...
field $install_paths : param //= ExtUtils::InstallPaths->new( dist_name => $meta->name );
field $verbose : param(v) //= 0;
field $dry_run : param //= 0;
field $pureperl : param //= 0;
field $jobs : param //= 1;
field $destdir : param //= '';
field $prefix : param //= '';
#
ADJUST {
-e 'META.json' or die "No META information provided\n";
# Configure Flags
my $is_bsd = $^O =~ /bsd/i;
my $is_win = $^O =~ /MSWin32/i;
$cflags = $is_bsd ? '' : '-fPIC ';
$ldflags = $is_bsd ? '' : ' -flto=auto ';
if ( $debug > 0 ) {
$cflags
.= '-DDEBUG=' .
$debug .
' -g3 -gdwarf-4 ' .
' -Wno-deprecated -pipe ' .
' -Wall -Wextra -Wpedantic -Wvla -Wnull-dereference ' .
' -Wswitch-enum -Wduplicated-cond ' .
' -Wduplicated-branches';
$cflags .= ' -fvar-tracking-assignments' unless $Config{osname} eq 'darwin';
}
elsif ( !$is_win ) {
$cflags
.= ' -DNDEBUG -DBOOST_DISABLE_ASSERTS -Ofast -ftree-vectorize -ffast-math -fno-align-functions -fno-align-loops -fno-omit-frame-pointer -flto=auto';
}
# Threading support (Critical for shm_open/librt on Linux)
if ( !$is_win ) {
$cflags .= ' -pthread';
$ldflags .= ' -pthread';
}
}
method write_file( $filename, $content ) { path($filename)->spew_raw($content) or die "Could not open $filename: $!\n" }
method read_file ($filename) { path($filename)->slurp_utf8 or die "Could not open $filename: $!\n" }
method step_build() {
$self->step_affix;
my %modules = map { $_ => catfile( 'blib', $_ ) } find( qr/\.pm$/, 'lib' );
my %docs = map { $_ => catfile( 'blib', $_ ) } find( qr/\.pod$/, 'lib' );
my %scripts = map { $_ => catfile( 'blib', $_ ) } find( qr/(?:)/, 'script' );
my %sdocs = map { $_ => delete $scripts{$_} } grep {/.pod$/} keys %scripts;
my %dist_shared = map { $_ => catfile( qw[blib lib auto share dist], $meta->name, abs2rel( $_, 'share' ) ) } find( qr/(?:)/, 'share' );
my %module_shared = map { $_ => catfile( qw[blib lib auto share module], abs2rel( $_, 'module-share' ) ) } find( qr/(?:)/, 'module-share' );
pm_to_blib( { %modules, %docs, %scripts, %dist_shared, %module_shared }, catdir(qw[blib lib auto]) );
make_executable($_) for values %scripts;
make_path( catdir(qw[blib arch]), { chmod => 0777, verbose => $verbose } );
0;
}
method step_clean() { remove_tree( $_, { verbose => $verbose } ) for qw[blib temp]; 0 }
method step_install() {
$self->step_build() unless -d 'blib';
my %res;
install(
[ from_to => $install_paths->install_map,
verbose => $verbose,
always_copy => 1,
dry_run => $dry_run,
uninst => $uninst,
result => \%res
]
);
# In the future, I might check the values of %res according to https://metacpan.org/pod/ExtUtils::Install#install
0;
}
method step_realclean () { remove_tree( $_, { verbose => $verbose } ) for qw[blib temp Build _build_params MYMETA.yml MYMETA.json]; 0 }
method step_test() {
$self->step_build() unless -d 'blib';
require TAP::Harness::Env;
my %test_args = (
( verbosity => $verbose ),
( jobs => $jobs ),
( color => -t STDOUT ),
lib => [ map { rel2abs( catdir( 'blib', $_ ) ) } qw[arch lib] ],
);
TAP::Harness::Env->create( \%test_args )->runtests( sort map { $_->stringify } find( qr/\.t$/, 't' ) )->has_errors;
}
method get_arguments (@sources) {
$_ = detildefy($_) for grep {defined} $install_base, $destdir, $prefix, values %{$install_paths};
$install_paths = ExtUtils::InstallPaths->new( dist_name => $meta->name );
return;
}
method Build(@args) {
my $method = $self->can( 'step_' . $action );
$method // die "No such action '$action'\n";
exit $method->($self);
}
method Build_PL() {
die "Pure perl Affix? Ha! You wish.\n" if $pureperl;
say sprintf 'Creating new Build script for %s %s', $meta->name, $meta->version;
$self->write_file( 'Build', sprintf <<'', $^X, __PACKAGE__, __PACKAGE__ );
#!%s
use lib 'builder';
use %s;
%s->new( @ARGV && $ARGV[0] =~ /\A\w+\z/ ? ( action => shift @ARGV ) : (),
map { /^--/ ? ( shift(@ARGV) =~ s[^--][]r => 1 ) : /^-/ ? ( shift(@ARGV) =~ s[^-][]r => shift @ARGV ) : () } @ARGV )->Build();
make_executable('Build');
my @env = defined $ENV{PERL_MB_OPT} ? split_like_shell( $ENV{PERL_MB_OPT} ) : ();
$self->write_file( '_build_params', encode_json( [ \@env, \@ARGV ] ) );
if ( my $dynamic = $meta->custom('x_dynamic_prereqs') ) {
my %meta = ( %{ $meta->as_struct }, dynamic_config => 1 );
$self->get_arguments( \@env, \@ARGV );
require CPAN::Requirements::Dynamic;
my $dynamic_parser = CPAN::Requirements::Dynamic->new();
my $prereq = $dynamic_parser->evaluate($dynamic);
$meta{prereqs} = $meta->effective_prereqs->with_merged_prereqs($prereq)->as_string_hash;
$meta = CPAN::Meta->new( \%meta );
}
$meta->save(@$_) for ['MYMETA.json'];
}
sub find ( $pattern, $base ) {
$base = path($base) unless builtin::blessed $base;
my $blah = $base->visit(
sub ( $path, $state ) {
$state->{$path} = $path if $path =~ $pattern;
},
{ recurse => 1 }
);
values %$blah;
}
# infix builder
method step_clone_infix() {
return if cwd->absolute->child('infix')->exists;
die 'Failed to clone infix' if system 'git clone --verbose https://github.com/sanko/infix.git';
}
method step_infix () {
$self->step_clone_infix();
my $cwd = cwd->absolute;
my $infix_dir = $cwd->child('infix');
# Use architecture-specific directory to avoid collision on shared filesystems (WSL vs Windows)
my $build_lib = $infix_dir->child( 'build_lib', $Config{archname} );
# If library already exists and is newer than source, we are good.
my $is_msvc = ( $Config{cc} =~ /cl(\.exe)?$/i );
my $lib_ext = $is_msvc ? '.lib' : '.a';
my $lib_pre = $is_msvc ? '' : 'lib';
my $lib_file = $build_lib->child( $lib_pre . 'infix' . $lib_ext );
my $src_file = $infix_dir->child( 'src', 'infix.c' );
if ( -e $lib_file && !$force ) {
# Check timestamps to ensure we rebuild if source changed
if ( $src_file->stat->mtime <= $lib_file->stat->mtime ) {
return 0;
}
}
#
warn "Building infix static library for $Config{archname}..." if $verbose;
$build_lib->mkpath unless -d $build_lib;
my @include_dirs = ( $infix_dir->child('include'), $infix_dir->child('src') );
# Detect Compiler settings using Perl's Config as base
my $cc_cmd = $Config{cc} || 'cc';
my $cc_type = 'gcc'; # Default flavor
if ( $cc_cmd =~ /cl(\.exe)?$/i ) { $cc_type = 'msvc'; }
elsif ( $cc_cmd =~ /clang/i ) { $cc_type = 'clang'; }
elsif ( $cc_cmd =~ /gcc/i ) { $cc_type = 'gcc'; }
elsif ( $cc_cmd =~ /egcc/i ) { $cc_type = 'gcc'; }
# Setup Flags
my ( $ar_cmd, @cflags, @arflags, $out_flag_cc, $out_flag_ar );
my @includes = map { ( $cc_type eq 'msvc' ? '/I' : '-I' ) . $_ } @include_dirs;
if ( $cc_type eq 'msvc' ) {
$ar_cmd = 'lib';
@cflags = ( '/nologo', '/c', '/std:c11', '/W3', '/GS', '/MD', '/O2', @includes );
@cflags = ( @cflags, '/DINFIX_DEBUG_ENABLED=1' ) if $verbose;
@arflags = ('/nologo');
$out_flag_cc = '/Fo';
$out_flag_ar = '/OUT:';
}
else {
# GCC / Clang
$ar_cmd = 'ar';
@cflags = ( '-std=c11', '-Wall', '-Wextra', '-O2', '-fPIC', @includes );
@cflags = ( @cflags, '-DINFIX_DEBUG_ENABLED=1' ) if $verbose;
@arflags = ('rcs');
$out_flag_cc = '-o';
$out_flag_ar = '';
# Pass pthread to compilation of static lib too (safe practice)
push @cflags, '-pthread' unless $^O eq 'MSWin32';
}
# Compile infix.c -> infix.o
my $obj_ext = $cc_type eq 'msvc' ? '.obj' : '.o';
my $obj_file = $build_lib->child( 'infix' . $obj_ext );
my @compile_cmd;
if ( $cc_type eq 'msvc' ) {
@compile_cmd = ( $cc_cmd, @cflags, $out_flag_cc . $obj_file, $src_file );
}
else {
@compile_cmd = ( $cc_cmd, @cflags, '-c', $src_file, $out_flag_cc, $obj_file );
}
warn " Compiling: @compile_cmd\n" if $verbose;
if ( system(@compile_cmd) != 0 ) {
die "Failed to compile infix.c";
}
# Archive infix.o -> libinfix.a
my @archive_cmd;
if ( $cc_type eq 'msvc' ) {
@archive_cmd = ( $ar_cmd, @arflags, $out_flag_ar . $lib_file, $obj_file );
}
else {
@archive_cmd = ( $ar_cmd, @arflags, $lib_file, $obj_file );
}
warn " Archiving: @archive_cmd\n" if $verbose;
if ( system(@archive_cmd) != 0 ) {
die "Failed to create infix static library";
}
warn "Infix library built: $lib_file\n" if $verbose;
return 0;
}
# Detects if linking against librt is required (common on Linux/BSD/Solaris for shm_open)
method check_for_lrt() {
return '' if $^O eq 'MSWin32';
warn "Checking if -lrt is required...\n" if $verbose;
my $cc = $Config{cc} || 'cc';
my $test_code = <<'END_C';
#include <sys/mman.h>
#include <fcntl.h>
( run in 0.741 second using v1.01-cache-2.11-cpan-39bf76dae61 )