view release on metacpan or search on metacpan
builder/Affix/Builder.pm view on Meta::CPAN
.= ' -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]) );
lib/Affix.c view on Meta::CPAN
UV uv = (UV)*s++;
// Handle Windows Surrogate Pairs (UTF-16LE)
if (sizeof(wchar_t) == 2 && uv >= 0xD800 && uv <= 0xDBFF) {
if (*s >= 0xDC00 && *s <= 0xDFFF) {
UV low = (UV)*s++;
uv = ((uv - 0xD800) << 10) + (low - 0xDC00) + 0x10000;
}
}
d = (char *)uvchr_to_utf8((U8 *)d, uv);
}
*d = 0;
// Set Perl SV properties
SvCUR_set(sv, d - SvPVX(sv));
SvPOK_on(sv);
SvUTF8_on(sv);
}
// Direct marshalling experiment
lib/Affix.c view on Meta::CPAN
step->executor(aTHX_ affix, step, &ST(0), args_buffer, c_args, ret_buffer); \
DISPATCH(); \
} \
CASE_OP_PUSH_PTR_WCHAR: \
{ \
SV * sv = ST(step->data.index); \
void * ptr = (char *)args_buffer + step->data.c_arg_offset; \
c_args[step->data.index] = ptr; \
if (SvPOK(sv)) { \
STRLEN len; \
U8 * s = (U8 *)SvPVutf8(sv, len); \
U8 * e = s + len; \
Newx(*(void **)ptr, len + 1, wchar_t); \
wchar_t * d = *(void **)ptr; \
while (s < e) { \
UV uv = utf8_to_uvchr_buf(s, e, nullptr); \
if (sizeof(wchar_t) == 2 && uv > 0xFFFF) { \
uv -= 0x10000; \
*d++ = (wchar_t)((uv >> 10) + 0xD800); \
*d++ = (wchar_t)((uv & 0x3FF) + 0xDC00); \
} \
else \
*d++ = (wchar_t)uv; \
s += UTF8SKIP(s); \
} \
*d = 0; \
lib/Affix/Build.pm view on Meta::CPAN
$_lib = (); # Reset cached library handle
my ( $path, $lang );
if ( ref $input eq 'SCALAR' ) { # Inline source code
$args{lang} // croak q[Parameter 'lang' (extension) is required for inline source];
$lang = lc $args{lang};
# Generate a unique filename in the build dir
state $counter = 0;
my $fname = sprintf( "source_%03d.%s", ++$counter, $lang );
$path = $build_dir->child($fname);
$path->spew_utf8($$input);
}
else { # File path
$path = Path::Tiny::path($input)->absolute;
croak "File not found: $path" unless $path->exists;
($lang) = $path =~ /\.([^.]+)$/;
$lang = lc( $lang // '' );
}
# Handle local flags
my $local_flags = $args{flags} // [];
lib/Affix/Build.pm view on Meta::CPAN
method _build_dotnet ( $src, $out, $mode, $lang ) {
my $file = $src->{path};
my $dotnet = $self->_can_run('dotnet') // croak "Dotnet not found";
my $proj_dir = $build_dir->child( "dotnet_${lang}_" . $self->_base($file) );
$proj_dir->mkpath;
$file->copy( $proj_dir->child( $file->basename ) );
my $ext = $lang eq 'fs' ? 'fsproj' : 'csproj';
my $proj = $proj_dir->child("Build.$ext");
my $lib_type = ( $mode eq 'dynamic' ) ? 'Shared' : 'Static';
my $items = $lang eq 'fs' ? '<ItemGroup><Compile Include="**/*.fs" /></ItemGroup>' : '';
$proj->spew_utf8(<<"XML");
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PublishAot>true</PublishAot>
<NativeLib>$lib_type</NativeLib>
<SelfContained>true</SelfContained>
</PropertyGroup>
$items
</Project>
XML
lib/Affix/Wrap.pm view on Meta::CPAN
args => \@args,
doc => $self->_doc_w_trail( $f, $s, $e ),
start_offset => $s,
end_offset => $e
);
}
method _get_content($f) {
my $abs = $self->_normalize($f);
return $file_cache->{$abs} if exists $file_cache->{$abs};
if ( -e $abs ) { return $file_cache->{$abs} = Path::Tiny::path($abs)->slurp_utf8; }
return '';
}
method _extract_doc( $f, $off ) {
return undef unless defined $off;
my $content = $self->_get_content($f);
return undef unless length($content);
my $pre = substr( $content, 0, $off );
my @lines = split /\n/, $pre;
my @d;
lib/Affix/Wrap.pm view on Meta::CPAN
if ( $f =~ /\.h(pp|xx)?$/i ) { $self->_scan( $f, \@objs ); $self->_scan_funcs( $f, \@objs ); }
else { $self->_scan_funcs( $f, \@objs ); }
}
@objs = sort { ( $a->file cmp $b->file ) || ( $a->start_offset <=> $b->start_offset ) } @objs;
@objs;
}
method _read($f) {
my $abs = $self->_normalize($f);
return $file_cache->{$abs} if exists $file_cache->{$abs};
return $file_cache->{$abs} = Path::Tiny::path($f)->slurp_utf8;
}
method _scan( $f, $acc ) {
my $c = $self->_read($f);
# Macros
while ( $c =~ /^\s*#\s*define\s+(\w+)(?:[ \t]+(.*?))?$/gm ) {
my $name = $1;
my $val = $2 // '';
my $s = $-[0];
lib/Affix/Wrap.pm view on Meta::CPAN
}
for my $node (@nodes) {
if ( ( $node isa Affix::Wrap::Typedef || $node isa Affix::Wrap::Struct || $node isa Affix::Wrap::Enum ) &&
exists $types->{ $node->name } ) {
next;
}
my $code = $node->affix_type;
if ($code) { $out .= "$code;\n"; }
}
$out .= "\n};\n1;\n";
Path::Tiny::path($file)->spew_utf8($out);
}
method wrap ( $lib, $target //= [caller]->[0] ) {
# Pre-register User Types
# This ensures they are available in the Affix registry before signatures are parsed,
# and allows using them in recursive definitions or opaque handles.
for my $name ( keys %$types ) {
my $type = $types->{$name};
my $type_str = builtin::blessed($type) ? $type : "$type";
lib/Test2/Tools/Affix.pm view on Meta::CPAN
else {
$opt = tempfile(
UNLINK => !$keep,
SUFFIX => '_' . path( [ caller() ]->[1] )->basename . ( $name =~ m[^\s*//\s*ext:\s*\.c$]ms ? '.c' : '.cxx' )
)->absolute;
push @cleanup, $opt unless $keep;
my ( $package, $filename, $line ) = caller;
$filename = path($filename)->canonpath;
$line++;
$filename =~ s[\\][\\\\]g; # Windows...
$opt->spew_utf8(qq[#line $line "$filename"\r\n$name]);
}
if ( !$opt ) {
$c->fail('Failed to locate test source');
$c->release;
return ();
}
$aggs->{cflags} .= ' -I' . $Inc;
my $compiler = Affix::Build->new( debug => 0, name => 'testing', version => '1.0', flags => $aggs );
$compiler->add( $opt->canonpath );
$compiler->link;
lib/Test2/Tools/Affix.pm view on Meta::CPAN
# $out =~ s[# Seeded srand with seed .+$][]m;
# $err =~ s[# Tests were run .+$][];
if ( $out =~ m[\S] ) {
$out =~ s[^((?:[ \t]*))(?=\S)][$1 ]gm;
print $out;
}
if ( $err =~ m[\S] ) {
$err =~ s[^((?:[ \t]*))(?=\S)][$1 ]gm;
print STDERR $err;
}
my $parsed = parse_xml( $report->slurp_utf8 );
# use Data::Dump;
# ddx $parsed;
# diag 'exit: '. $exit;
# Test2::API::test2_stack()->top->{count}++;
ok !$exit && !$parsed->{valgrindoutput}{errorcounts}, $name;
}
}
END {
t/017_affix_build.t view on Meta::CPAN
= $lang eq 'rust' ? 'rs' :
$lang eq 'csharp' ? 'cs' :
$lang eq 'fsharp' ? 'fs' :
$lang eq 'fortran' ? 'f90' :
$lang eq 'pascal' ? 'pas' :
$lang eq 'crystal' ? 'cr' :
$lang eq 'assembly' ? 'asm' :
$lang eq 'cobol' ? 'cbl' :
$lang;
my $src = $TMP_DIR->child("test_$lang.$ext");
$src->spew_utf8($code);
#
my $c = Affix::Build->new( build_dir => $TMP_DIR, name => "${lang}_lib" );
$c->add($src);
try { $c->compile_and_link() }
catch ($err) {
skip_all 'Link failed (toolchain issue?): ' . $err;
return;
}
pass 'Linked successfully';
#
t/017_affix_build.t view on Meta::CPAN
PROCEDURE DIVISION USING A, B, R.
ADD A TO B GIVING R.
GOBACK.
END PROGRAM add_cob.
subtest 'Polyglot: Number Cruncher (C + Fortran + ASM)' => sub {
skip_all "Missing compilers" unless bin_path( $Config{cc} ) && bin_path('gfortran');
# C is our orchestrator
my $c_src = $TMP_DIR->child('math_core.c');
$c_src->spew_utf8(<<~'C');
#include <stdio.h>
#ifdef _WIN32
__declspec(dllexport)
#endif
int core_version() { return 1; }
C
# Fortran does the math
my $f_src = $TMP_DIR->child('math_algos.f90');
$f_src->spew_utf8(<<~'F90');
function fortran_add(a, b) bind(c, name='fortran_add')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: fortran_add
fortran_add = a + b
end function
F90
# Assembly for optimization
my $asm_bin;
t/017_affix_build.t view on Meta::CPAN
$asm_src = <<~'' }
global asm_inc
section .text
asm_inc:
mov eax, edi
inc eax
ret
skip_all "Missing Assembler ($asm_bin)" unless bin_path($asm_bin);
my $asm_file = $TMP_DIR->child($asm_file_name);
$asm_file->spew_utf8($asm_src);
#
my $compiler = Affix::Build->new( name => 'number_cruncher', build_dir => $TMP_DIR );
$compiler->add($c_src);
$compiler->add($f_src);
$compiler->add($asm_file);
ok( lives { $compiler->link() }, 'Linked Number Cruncher' ) or note $@;
ok( $compiler->libname->exists, 'Library exists' );
enjoin( $compiler->libname, 'core_version', 'fortran_add', 'asm_inc' );
};
subtest 'Polyglot: Modern Stack (C++ + Rust + Zig)' => sub {
skip_all 'Missing compilers' unless bin_path('g++') && bin_path('rustc') && bin_path('zig');
skip_all 'Rust/MinGW target missing' unless check_rust_gnu();
# C++ for ease of ABI
my $cpp_src = $TMP_DIR->child('interface.cpp');
$cpp_src->spew_utf8(<<~'');
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#endif
int cpp_interface() { return 2025; }
}
# Rust for safety
my $rs_src = $TMP_DIR->child('safety.rs');
$rs_src->spew_utf8(<<~'');
#[no_mangle]
pub extern "C" fn rust_safe_add(a: i32, b: i32) -> i32 {
a + b
}
# Zig for logic
my $zig_src = $TMP_DIR->child('logic.zig');
$zig_src->spew_utf8(<<~'');
export fn zig_calc() i32 {
return 42;
}
#
my $compiler = Affix::Build->new( name => 'modern_stack', build_dir => $TMP_DIR );
$compiler->add($cpp_src);
$compiler->add($rs_src);
$compiler->add($zig_src);
ok( lives { $compiler->link() }, 'Linked Modern Stack' ) or note $@;
t/017_affix_build.t view on Meta::CPAN
push @reqs, $Config{cc}; # System CC
push @reqs, ( $Config{archname} =~ /arm64/ ? $Config{cc} : 'nasm' );
push @reqs, 'rustc';
for my $bin (@reqs) {
skip_all "Missing $bin" unless bin_path($bin);
}
skip_all 'Rust/MinGW target missing' unless check_rust_gnu();
my $c = Affix::Build->new( name => 'mega_lib', build_dir => $TMP_DIR );
#
my $f1 = $TMP_DIR->child('f1.c');
$f1->spew_utf8(<<~'');
#ifdef _WIN32
__declspec(dllexport)
#endif
int func_c( ) { return 1; }
$c->add($f1);
#
my $f2 = $TMP_DIR->child('f2.cpp');
$f2->spew_utf8(<<~'');
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#endif
int func_cpp( ) { return 2; }
}
$c->add($f2);
#
my $f3 = $TMP_DIR->child('f3.rs');
$f3->spew_utf8(<<~'');
#[no_mangle]
pub extern "C" fn func_rs( )->i32{ 3 }
$c->add($f3);
#
my $f4 = $TMP_DIR->child('f4.zig');
$f4->spew_utf8(<<~'');
export fn func_zig() i32 { return 4; }
$c->add($f4);
#
my $f5 = $TMP_DIR->child('f5.d');
$f5->spew_utf8( ( $^O eq 'MSWin32' ? <<~'' : '' ) . <<~'' );
import core.sys.windows.dll;
mixin SimpleDllMain;
export
extern(C) int func_d() { return 5; }
$c->add($f5);
#
my $f6 = $TMP_DIR->child('f6.f90');
$f6->spew_utf8(<<~'');
function func_f() bind(c, name='func_f')
use iso_c_binding
integer(c_int) :: func_f
func_f=6
end function
$c->add($f6);
#
my $asm_ext = ( $Config{archname} =~ /arm64/ ) ? 's' : 'asm';
my $f7 = $TMP_DIR->child("f7.$asm_ext");
$f7->spew_utf8( ( $^O eq 'MSWin32' || $Config{archname} !~ /arm64/ ) ? <<~'' : <<~'' );
; x86/x64
global func_asm
section .text
func_asm:
mov eax, 7
ret
; ARM64
.global func_asm
.text
t/025_affix_wrap.t view on Meta::CPAN
use Path::Tiny;
use Capture::Tiny qw[capture];
# Determine if Clang is available
my $CLANG_AVAIL = do {
my ( undef, undef, $exit ) = capture { system 'clang', '--version' };
$exit == 0;
};
sub spew_files ( $dir, %files ) {
$dir->child($_)->spew_utf8( $files{$_} ) for keys %files;
$dir;
}
sub run_tests_for_driver ( $driver_class, $label ) {
subtest 'Driver: ' . $label => sub {
subtest 'Preprocessor & Defines' => sub {
my $dir = Path::Tiny->tempdir;
spew_files(
$dir,
'defs.h' => <<'EOF',
t/025_affix_wrap.t view on Meta::CPAN
typedef struct { int x; } StaticStruct;
int static_func(int i);
EOF
'main.c' => '#include "static.h"'
);
my $parser = $driver_class->new( project_files => [ $dir->child('static.h')->stringify ] );
my $binder = Affix::Wrap->new( driver => $parser );
my $pm_file = $dir->child('StaticLib.pm');
$binder->generate( 'dummy_lib', 'StaticLib', $pm_file->stringify );
ok -e $pm_file, 'Generated .pm file';
my $content = $pm_file->slurp_utf8;
like $content, qr/package\s+StaticLib\s*{/, 'Package decl';
like $content, qr/use constant STATIC_VAL => 42;/, 'Constant generated';
like $content, qr/typedef 'StaticStruct' => Struct\[ x => Int \];/, 'Struct typedef generated';
like $content, qr/affix \$lib, ('static_func'|\[_static_func => 'static_func'\]) => \[Int\], Int;/, 'Function affix generated';
# Syntax check
my ( undef, undef, $exit ) = capture { system $^X, '-Ilib', '-c', $pm_file->stringify };
is $exit >> 8, 0, 'Generated code syntax check OK';
};
};
t/050_affix_build.t view on Meta::CPAN
= $lang eq 'rust' ? 'rs' :
$lang eq 'csharp' ? 'cs' :
$lang eq 'fsharp' ? 'fs' :
$lang eq 'fortran' ? 'f90' :
$lang eq 'pascal' ? 'pas' :
$lang eq 'crystal' ? 'cr' :
$lang eq 'assembly' ? 'asm' :
$lang eq 'cobol' ? 'cbl' :
$lang;
my $src = $TMP_DIR->child("test_$lang.$ext");
$src->spew_utf8($code);
#
my $c = Affix::Build->new( build_dir => $TMP_DIR, name => "${lang}_lib" );
$c->add($src);
try { $c->compile_and_link() }
catch ($err) {
skip_all 'Link failed (toolchain issue?): ' . $err;
return;
}
pass 'Linked successfully';
#
t/050_affix_build.t view on Meta::CPAN
PROCEDURE DIVISION USING A, B, R.
ADD A TO B GIVING R.
GOBACK.
END PROGRAM add_cob.
subtest 'Polyglot: Number Cruncher (C + Fortran + ASM)' => sub {
skip_all "Missing compilers" unless bin_path( $Config{cc} ) && bin_path('gfortran');
# C is our orchestrator
my $c_src = $TMP_DIR->child('math_core.c');
$c_src->spew_utf8(<<~'C');
#include <stdio.h>
#ifdef _WIN32
__declspec(dllexport)
#endif
int core_version() { return 1; }
C
# Fortran does the math
my $f_src = $TMP_DIR->child('math_algos.f90');
$f_src->spew_utf8(<<~'F90');
function fortran_add(a, b) bind(c, name='fortran_add')
use iso_c_binding
integer(c_int), value :: a, b
integer(c_int) :: fortran_add
fortran_add = a + b
end function
F90
# Assembly for optimization
my $asm_bin;
t/050_affix_build.t view on Meta::CPAN
$asm_src = <<~'' }
global asm_inc
section .text
asm_inc:
mov eax, edi
inc eax
ret
skip_all "Missing Assembler ($asm_bin)" unless bin_path($asm_bin);
my $asm_file = $TMP_DIR->child($asm_file_name);
$asm_file->spew_utf8($asm_src);
#
my $compiler = Affix::Build->new( name => 'number_cruncher', build_dir => $TMP_DIR );
$compiler->add($c_src);
$compiler->add($f_src);
$compiler->add($asm_file);
ok( lives { $compiler->link() }, 'Linked Number Cruncher' ) or note $@;
ok( $compiler->libname->exists, 'Library exists' );
enjoin( $compiler->libname, 'core_version', 'fortran_add', 'asm_inc' );
};
subtest 'Polyglot: Modern Stack (C++ + Rust + Zig)' => sub {
skip_all 'Missing compilers' unless bin_path('g++') && bin_path('rustc') && bin_path('zig');
skip_all 'Rust/MinGW target missing' unless check_rust_gnu();
# C++ for ease of ABI
my $cpp_src = $TMP_DIR->child('interface.cpp');
$cpp_src->spew_utf8(<<~'');
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#endif
int cpp_interface() { return 2025; }
}
# Rust for safety
my $rs_src = $TMP_DIR->child('safety.rs');
$rs_src->spew_utf8(<<~'');
#[no_mangle]
pub extern "C" fn rust_safe_add(a: i32, b: i32) -> i32 {
a + b
}
# Zig for logic
my $zig_src = $TMP_DIR->child('logic.zig');
$zig_src->spew_utf8(<<~'');
export fn zig_calc() i32 {
return 42;
}
#
my $compiler = Affix::Build->new( name => 'modern_stack', build_dir => $TMP_DIR );
$compiler->add($cpp_src);
$compiler->add($rs_src);
$compiler->add($zig_src);
ok( lives { $compiler->link() }, 'Linked Modern Stack' ) or note $@;
t/050_affix_build.t view on Meta::CPAN
push @reqs, $Config{cc}; # System CC
push @reqs, ( $Config{archname} =~ /arm64/ ? $Config{cc} : 'nasm' );
push @reqs, 'rustc';
for my $bin (@reqs) {
skip_all "Missing $bin" unless bin_path($bin);
}
skip_all 'Rust/MinGW target missing' unless check_rust_gnu();
my $c = Affix::Build->new( name => 'mega_lib', build_dir => $TMP_DIR );
#
my $f1 = $TMP_DIR->child('f1.c');
$f1->spew_utf8(<<~'');
#ifdef _WIN32
__declspec(dllexport)
#endif
int func_c( ) { return 1; }
$c->add($f1);
#
my $f2 = $TMP_DIR->child('f2.cpp');
$f2->spew_utf8(<<~'');
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#endif
int func_cpp( ) { return 2; }
}
$c->add($f2);
#
my $f3 = $TMP_DIR->child('f3.rs');
$f3->spew_utf8(<<~'');
#[no_mangle]
pub extern "C" fn func_rs( )->i32{ 3 }
$c->add($f3);
#
my $f4 = $TMP_DIR->child('f4.zig');
$f4->spew_utf8(<<~'');
export fn func_zig() i32 { return 4; }
$c->add($f4);
#
my $f5 = $TMP_DIR->child('f5.d');
$f5->spew_utf8( ( $^O eq 'MSWin32' ? <<~'' : '' ) . <<~'' );
import core.sys.windows.dll;
mixin SimpleDllMain;
export
extern(C) int func_d() { return 5; }
$c->add($f5);
#
my $f6 = $TMP_DIR->child('f6.f90');
$f6->spew_utf8(<<~'');
function func_f() bind(c, name='func_f')
use iso_c_binding
integer(c_int) :: func_f
func_f=6
end function
$c->add($f6);
#
my $asm_ext = ( $Config{archname} =~ /arm64/ ) ? 's' : 'asm';
my $f7 = $TMP_DIR->child("f7.$asm_ext");
$f7->spew_utf8( ( $^O eq 'MSWin32' || $Config{archname} !~ /arm64/ ) ? <<~'' : <<~'' );
; x86/x64
global func_asm
section .text
func_asm:
mov eax, 7
ret
; ARM64
.global func_asm
.text