Affix

 view release on metacpan or  search on metacpan

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

            push @cmd, $os eq 'MSWin32' ? ('-shared') : ( '-shared', '-fPIC' );
            push @cmd, '-Wl,--export-all-symbols' if $os eq 'MSWin32' && $linker =~ /gcc|g\+\+|clang/;
            push @cmd, '-o', $libname->stringify;

            # MinGW Static Lib Fix: --whole-archive ensures unused symbols (like language runtimes) are kept
            my $is_gcc = ( $linker =~ /gcc|g\+\+|clang/ || $Config{cc} =~ /gcc/ );
            foreach my $f (@files) {
                my $p = "$f";
                if ( $is_gcc && $p =~ /\Q$Config{_a}\E$/ ) {
                    push @cmd, '-Wl,--whole-archive', $p, '-Wl,--no-whole-archive';
                }
                else {
                    push @cmd, $p;
                }
            }
            my %seen;
            for my $l (@libs) {
                next if $seen{$l}++;
                push @cmd, "-l$l";
            }
            push @cmd, @ldflags;
            $self->_run(@cmd);
            return $libname;
        }

        # Helper to map extensions to method names
        method _resolve_handler ($l) {

            # Normalize
            if    ( $l =~ /^(cpp|cxx|cc|c\+\+)$/ )      { return '_build_cpp'; }
            elsif ( $l =~ /^(f|f90|f95|for|fortran)$/ ) { return '_build_fortran'; }
            elsif ( $l =~ /^(ru?st?)$/ )                { return '_build_rust'; }
            elsif ( $l =~ /^(cs|csharp|c#)$/ )          { return '_build_csharp'; }
            elsif ( $l =~ /^(fs|fsharp|f#)$/ )          { return '_build_fsharp'; }
            elsif ( $l =~ /^(s|asm|assembly)$/ )        { return '_build_asm'; }
            elsif ( $l =~ /^(adb|ads|ada)$/ )           { return '_build_ada'; }
            elsif ( $l =~ /^(hs|lhs|haskell)$/ )        { return '_build_haskell'; }
            elsif ( $l =~ /^(cr|crystal)$/ )            { return '_build_crystal'; }
            elsif ( $l =~ /^(fut|futhark)$/ )           { return '_build_futhark'; }
            elsif ( $l =~ /^(pas|pp|pascal)$/ )         { return '_build_pascal'; }
            elsif ( $l =~ /^(cbl|cob|cobol)$/ )         { return '_build_cobol'; }
            elsif ( $l =~ /^(ml|ocaml)$/ )              { return '_build_ocaml'; }
            elsif ( $l =~ /^(e|eiffel)$/ )              { return '_build_eiffel'; }
            elsif ( $l =~ /^(go)$/ )                    { return '_build_go'; }
            elsif ( $l =~ /^(zig)$/ )                   { return '_build_zig'; }
            elsif ( $l =~ /^(odin)$/ )                  { return '_build_odin'; }
            elsif ( $l =~ /^(nim)$/ )                   { return '_build_nim'; }
            elsif ( $l =~ /^(d|dlang)$/ )               { return '_build_d'; }
            elsif ( $l =~ /^(swift)$/ )                 { return '_build_swift'; }
            elsif ( $l =~ /^(v|vlang)$/ )               { return '_build_v'; }

            # Fallback to C
            return '_build_c';
        }

        method _run (@cmd) {
            print STDERR "[Affix] Exec: @cmd\n" if $debug;

            # use Data::Dump;
            # ddx \@cmd;
            my ( $stdout, $stderr, $exit ) = capture {
                system @cmd;
            };
            if ( !!$exit ) {
                warn $stdout if $stdout;
                warn $stderr if $stderr;
                my $rc = $exit >> 8;
                croak "Command failed (Exit $rc): @cmd";
            }
        }

        method _can_run (@cmd) {
            for my $c (@cmd) {
                return $c if MM->maybe_command($c);
                for my $dir ( File::Spec->path ) {
                    my $abs = File::Spec->catfile( $dir, $c );
                    return $abs if MM->maybe_command($abs);
                }
            }
            return undef;
        }
        method _base ($file) { return $file->basename(qr/\.[^.]+$/); }
        #
        method _build_c ( $src, $out, $mode ) {
            my $file  = $src->{path};
            my @local = @{ $src->{flags} };
            my $cc    = $Config{cc} // 'cc';
            if ( $mode eq 'dynamic' ) {

                # Combine Global CFLAGS + Local Flags + Global LDFLAGS
                my @cmd = ( $cc, '-shared', @cflags, @local, "$file", '-o', "$out", @ldflags );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                return $out;
            }
            else {
                my $obj = $build_dir->child( $self->_base($file) . $Config{_o} );

                # Combine Global CFLAGS + Local Flags
                my @cmd = ( $cc, '-c', @cflags, @local, "$file", '-o', "$obj" );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                return { file => $obj };
            }
        }

        method _build_cpp ( $src, $out, $mode ) {
            my $file  = $src->{path};
            my @local = @{ $src->{flags} };
            my $cxx   = ( $Config{cc} =~ /gcc/ ) ? 'g++' : ( ( $Config{cc} =~ /clang/ ) ? 'clang++' : 'c++' );
            if ( $mode eq 'dynamic' ) {
                my @cmd = ( $cxx, '-shared', @cxxflags, @local, "$file", '-o', "$out", @ldflags );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                return $out;
            }
            else {
                my $obj = $build_dir->child( $self->_base($file) . $Config{_o} );
                my @cmd = ( $cxx, '-c', @cxxflags, @local, "$file", '-o', "$obj" );
                push @cmd, '-fPIC' unless $os eq 'MSWin32';
                $self->_run(@cmd);
                return { file => $obj };
            }
        }

        #~ https://fasterthanli.me/series/making-our-own-executable-packer/part-5



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