Alien-Xmake

 view release on metacpan or  search on metacpan

eg/nuklear/nuklear.pl  view on Meta::CPAN

use v5.40;
use blib;
use FindBin qw[$RealBin];
use Affix   qw[:all];
use File::Spec;
use File::Temp qw[tempdir tempfile];
use File::Copy qw[copy];
use Cwd        qw[getcwd];
use Alien::Xmake;
my $_start_cwd = getcwd;

# nuklear window-flags
use constant {
    NK_WINDOW_BORDER      => 0x01,
    NK_WINDOW_MOVABLE     => 0x02,
    NK_WINDOW_SCALABLE    => 0x04,
    NK_WINDOW_CLOSABLE    => 0x08,
    NK_WINDOW_MINIMIZABLE => 0x10,
    NK_WINDOW_TITLE       => 0x40,
    NK_TEXT_LEFT          => 0x11,
    NK_TEXT_CENTERED      => 0x12,
    NK_TEXT_RIGHT         => 0x14
};

# Builds the small C shim (nk_win.c) into a shared library, installing the result into this
# script's own directory so it is found deterministically. The build runs in a temp directory
# (never in place) so xmake does not walk up and mistake a parent xmake.lua for the project root.
# Because Capture::Tiny swallows xmake's interactive "y/n/m" prompt for first-time package
# installs, we feed a stream of "y" answers to stdin and pass --yes / --confirm=yes so nothing ever
# hangs waiting on a TTY. Well, that's the plan anyway...
my $src_dir  = $RealBin;
my $dest_dir = $RealBin;                                                         # install the .dll/.so next to this script
my $ext      = $^O eq 'MSWin32' ? '.dll' : $^O eq 'darwin' ? '.dylib' : '.so';
my $artifact = File::Spec->catfile( $dest_dir, "nk_win$ext" );
my $tool     = Alien::Xmake->new( verbose => 0, yes => 1 );
die 'no xmake available via Alien::Xmake' unless $tool->exe;

# Run `xmake <action>` in $dir (a temp copy of the project), feeding repeated "y" answers on stdin.
# xmake's first-run package install reads io.read() and ignores the global --yes/-y flag, so a
# piped/redirected "y" is the only way to answer it without a TTY. We redirect stdin from a temp
# file of "y"s via the shell; stdout/stderr are inherited so the user sees xmake's progress.
sub run_xmake ( $dir, @args ) {
    local $ENV{XMAKE_THEME} = 'plain';
    my ( $fh, $yfile ) = tempfile( SUFFIX => '.in', UNLINK => 0 );
    print {$fh} ("y\n") x 16;
    close $fh  or die "close $yfile: $!";
    chdir $dir or die "chdir $dir: $!";
    my $cmd = join ' ', map { /\s/ ? '"' . $_ . '"' : $_ } ( $tool->exe, @args );
    return system("$cmd < $yfile") == 0;
}

# ensure the shim sources are present
die "Missing $src_dir/nk_win.c\n" unless -f File::Spec->catfile( $src_dir, 'nk_win.c' );
if ( !-f $artifact ) {
    say 'Building nk_win shim (this installs nuklear on first run)...';
    my $work = tempdir( CLEANUP => 1 );
    copy( File::Spec->catfile( $src_dir, $_ ), $work ) for qw[ nk_win.c nuklear_gdi.h nuklear_xlib.h xmake.lua ];

    # configure step; this is where the nuklear package is fetched/installed
    run_xmake( $work, 'f', '-m', 'release' ) or die 'xmake config failed';

    # build the shared library
    run_xmake( $work, 'build' ) or die 'xmake build failed';

    # locate the produced shared library
    my ( $plat, $arch ) = ( $^O eq 'MSWin32' ? ( 'windows', 'x64' ) : ( 'linux', 'x86_64' ) );
    ( $plat, $arch ) = ( 'macosx', 'x86_64' ) if $^O eq 'darwin';
    my $built = File::Spec->catfile( $work, 'build', $plat, $arch, 'release', "nk_win$ext" );
    die "built library not found: $built" unless -f $built;
    copy( $built, $artifact ) or die "copy to $artifact: $!";
    chdir $_start_cwd;    # leave the temp dir so File::Temp can clean it up
    say "Installed $artifact";
}
my $dll = $artifact;
die "nk_win$ext not found at $dll" unless -f $dll;

# wrapper (single window, message pump)
affix $dll, 'nk_win_init',     [ WString, Int, Int, String, Int ]                           => Int;
affix $dll, 'nk_win_poll',     []                                                           => Void;
affix $dll, 'nk_win_running',  []                                                           => Int;
affix $dll, 'nk_win_render',   [ Int, Int, Int, Int ]                                       => Void;
affix $dll, 'nk_win_shutdown', []                                                           => Void;
affix $dll, 'nk_win_context',  []                                                           => Pointer [Void];
affix $dll, 'nk_win_begin',    [ Pointer [Void], String, Float, Float, Float, Float, UInt ] => Int;

# nuklear API (direct exports)
affix $dll, 'nk_end',                [ Pointer [Void] ]                                => Void;
affix $dll, 'nk_label',              [ Pointer [Void], String, Int ]                   => Void;
affix $dll, 'nk_button_label',       [ Pointer [Void], String ]                        => Int;
affix $dll, 'nk_layout_row_dynamic', [ Pointer [Void], Float, Int ]                    => Void;
affix $dll, 'nk_slide_float',        [ Pointer [Void], Float, Float, Float, Float ]    => Float;
affix $dll, 'nk_checkbox_label',     [ Pointer [Void], String, Pointer [Int] ]         => Int;
affix $dll, 'nk_option_label',       [ Pointer [Void], String, Int ]                   => Int;
affix $dll, 'nk_progress',           [ Pointer [Void], Pointer [Size_t], Size_t, Int ] => Int;
affix $dll, 'nk_group_begin',        [ Pointer [Void], String, Int ]                   => Int;
affix $dll, 'nk_group_end',          [ Pointer [Void] ]                                => Void;
affix $dll, 'nk_spacing',            [ Pointer [Void], Int ]                           => Void;
die 'failed to open window' unless nk_win_init( 'Affix + Nuklear', 900, 640, 'Arial', 14 );
my $ctx = nk_win_context();

# Idempotent teardown: nk_win_shutdown() is unsafe to call twice, and the END block runs it again



( run in 1.546 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )