Affix

 view release on metacpan or  search on metacpan

lib/Affix/Build.pm  view on Meta::CPAN

        #~ https://fasterthanli.me/series/making-our-own-executable-packer/part-5
        #~ https://stackoverflow.com/questions/71704813/writing-and-linking-shared-libraries-in-assembly-32-bit
        #~ https://github.com/therealdreg/nasm_linux_x86_64_pure_sharedlib
        method _build_asm ( $src, $out, $mode ) {
            my $file  = $src->{path};
            my @local = @{ $src->{flags} };
            my $obj   = $build_dir->child( $self->_base($file) . $Config{_o} );

            # Detect Assembler Type: .asm (Intel/NASM) vs .s (AT&T/CC)
            my $is_nasm  = ( $file =~ /\.asm$/i );
            my $compiled = 0;
            if ($is_nasm) {    # .asm = Intel syntax = NASM
                my $nasm = $self->_can_run('nasm') // croak "NASM not found";
                my $fmt  = $os eq 'MSWin32' ? 'win64' : ( $os eq 'darwin' ? 'macho64' : 'elf64' );
                $self->_run( $nasm, '-f', $fmt, @local, "$file", '-o', "$obj" );
                $compiled = 1;
            }
            else {             # .s = AT&T/GNU syntax = System CC
                my $cc = $Config{cc};
                if ( $cc && $self->_can_run($cc) ) {
                    my @cmd = ( $cc, '-c', @local, "$file", '-o', "$obj" );
                    push @cmd, '-fPIC' unless $os eq 'MSWin32';
                    $self->_run(@cmd);
                    $compiled = 1;
                }
            }
            croak 'Assembly failed' unless $compiled && -e "$obj";
            if ( $mode eq 'dynamic' ) {
                my @cmd = ( $linker, '-shared', "$obj", '-o', "$out", @ldflags );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                if ( $os eq 'MSWin32' && $linker =~ /gcc|g\+\+/ ) {
                    push @cmd, '-Wl,--export-all-symbols';
                }
                $self->_run(@cmd);
                return $out;
            }
            return { file => $obj };
        }

        #~ https://blog.asleson.org/2021/02/23/how-to-writing-a-c-shared-library-in-rust/
        method _build_rust ( $src, $out, $mode ) {
            my $file  = $src->{path};
            my @local = @{ $src->{flags} };
            my $rc    = $self->_can_run('rustc') // croak "Rustc not found";
            if ( $mode eq 'dynamic' ) {
                $self->_run( $rc, '--crate-type', 'cdylib', @local, '-o', "$out", "$file" );
                return $out;
            }
            else {
                my $lib = $build_dir->child( $self->_base($file) . $Config{_a} );
                my @cmd = ( $rc, '--crate-type=staticlib', '--emit=link', '-C', 'panic=abort', @local, "$file", '-o', "$lib" );

                # Force GNU target on MinGW to ensure compatibility with Perl's linker
                if ( $os eq 'MSWin32' && $Config{cc} =~ /gcc/ ) {
                    push @cmd, '--target', 'x86_64-pc-windows-gnu';
                }
                elsif ( $os ne 'MSWin32' ) {
                    push @cmd, '-C', 'relocation-model=pic';
                }
                $self->_run(@cmd);
                my @deps = $os eq 'MSWin32' ? qw(ws2_32 userenv bcrypt advapi32 ntdll) : qw(dl pthread m);
                return { file => $lib, libs => \@deps };
            }
        }

        #~ https://medium.com/@walkert/fun-building-shared-libraries-in-go-639500a6a669
        #~ https://github.com/vladimirvivien/go-cshared-examples
        method _build_go ( $src, $out, $mode ) {
            my $file  = $src->{path};
            my @local = @{ $src->{flags} };    # passed to go build args

            # MinGW GCC 8.3.0 had known issues guaranteeing the 16-byte stack alignment required by the
            # Go runtime (and SSE/AVX instructions) on Windows x64. If Perl calls your library with a
            # 8-byte aligned stack (which was common in older GCC optimization flags), Go will segfault
            # immediately when it tries to access the stack.
            push @local, q[-ldflags "-extldflags '-static -static-libgcc -static-libstdc++'"] if $^O eq 'MSWin32';
            if ( $mode eq 'dynamic' ) {
                $self->_run( 'go', 'build', '-buildmode=c-shared', @local, '-o', "$out", "$file" );
                return $out;
            }
            else {
                my $lib = $build_dir->child( $self->_base($file) . $Config{_a} );
                $self->_run( 'go', 'build', '-buildmode=c-archive', @local, '-o', "$lib", "$file" );
                return { file => $lib, libs => ['pthread'] };
            }
        }

        #~ https://odin-lang.org/news/calling-odin-from-python/
        #~ https://odin-lang.org/docs/install/#release-requirements--notes
        method _build_odin ( $src, $out, $mode ) {
            my $file  = $src->{path};
            my @local = @{ $src->{flags} };
            my $odin  = $self->_can_run('odin') // croak "Odin not found";
            if ( $mode eq 'dynamic' ) {
                $self->_run( $odin, 'build', "$file", '-file', '-build-mode:dll', @local, "-out:$out" );
                return $out;
            }
            else {
                my $obj = $build_dir->child( $self->_base($file) . $Config{_o} );
                my @cmd = ( $odin, 'build', "$file", '-file', '-build-mode:obj', @local, "-out:$obj" );
                push @cmd, '-reloc-mode:pic' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                unless ( $obj->exists ) {    # Attempt to find it if Odin misnamed it (e.g. .obj vs .o)
                    my $cwd_obj = Path::Tiny::path( $self->_base($file) . $Config{_o} );
                    $cwd_obj->move($obj) if $cwd_obj->exists;
                }
                return { file => $obj };
            }
        }

        #~ https://dlang.org/articles/dll-linux.html#dso9
        #~ dmd -c dll.d -fPIC
        #~ dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is
        method _build_d ( $src, $out, $mode ) {
            my $file = $src->{path};
            my $dmd  = $self->_can_run(qw[dmd ldc2 gdc]) // croak "D compiler not found";
            if ( $mode eq 'dynamic' ) {
                my @cmd = ( $dmd, '-shared', "$file", "-of=$out" );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                return $out;
            }
            else {
                my $lib = $build_dir->child( $self->_base($file) . $Config{_a} );
                my @cmd = ( $dmd, '-lib', "$file", "-of=$lib" );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                return { file => $lib };
            }
        }
        method _build_csharp ( $file, $out, $mode ) { $self->_build_dotnet( $file, $out, $mode, 'cs' ); }

        #~ https://github.com/secana/Native-FSharp-Library
        #~ https://secanablog.wordpress.com/2020/02/01/writing-a-native-library-in-f-which-can-be-called-from-c/
        method _build_fsharp ( $file, $out, $mode ) { $self->_build_dotnet( $file, $out, $mode, 'fs' ); }

        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");



( run in 0.590 second using v1.01-cache-2.11-cpan-39bf76dae61 )