PAX
view release on metacpan or search on metacpan
lib/PAX/StandaloneRuntime.pm view on Meta::CPAN
stopped => 'ð¨',
paused => 'ð¨',
insecure => 'ð¨',
reloading => 'ð¨',
missing => 'ð¨',
error => 'ð¨',
dirty => 'ð¨',
down => 'ð¨',
},
};
sub _trace {
return if !$ENV{PAX_STANDALONE_TRACE};
my ($message) = @_;
$message //= '';
print STDERR "[pax-standalone] $message\n";
}
sub _capture_system_command {
my (@command) = @_;
my $exit_code = -1;
my ($stdout, $stderr) = Capture::Tiny::capture {
system @command;
$exit_code = $? == -1 ? -1 : ($? >> 8);
};
return ($stdout, $stderr, $exit_code);
}
sub _system_command_missing {
my ($stderr, $exit_code) = @_;
return 1 if !defined $exit_code || $exit_code < 0 || $exit_code == 127;
return 1 if defined $stderr && $stderr =~ /(?:can't exec|not found|no such file or directory)/i;
return 0;
}
sub run {
my ($class, %args) = @_;
my $entrypoint = $args{entrypoint} // shift(@ARGV);
_state();
if (!defined $entrypoint || !_entrypoint_looks_valid($entrypoint)) {
my $fallback = _resolve_entrypoint_from_manifest($entrypoint);
if (defined $fallback) {
_trace("entrypoint fallback from manifest: '" . ($entrypoint // '<undef>') . "' -> '$fallback'");
$entrypoint = $fallback;
}
}
die 'entrypoint required' if !defined $entrypoint;
if (!_entrypoint_looks_valid($entrypoint)) {
die "entrypoint is not a valid executable unit: $entrypoint";
}
my @argv = @{ $args{argv} // \@ARGV };
my $self_path = _standalone_executable_path();
_install_namespace_compat();
_install_require_hook();
_install_pending_wrappers();
local $0 = $self_path if defined $self_path && $self_path ne '';
if (@argv && $argv[0] eq '--pax-standalone-helper') {
shift @argv;
my $helper = shift @argv // die "standalone helper name required\n";
local @ARGV = @argv;
return _run_standalone_managed_helper($helper, @argv);
}
local @ARGV = @argv;
my $rv = _run_entrypoint($entrypoint);
_install_pending_wrappers();
return $rv;
}
sub _entrypoint_looks_valid {
my ($entrypoint) = @_;
return 0 if !defined $entrypoint;
return 0 if $entrypoint =~ /\A-/;
return 0 if $entrypoint =~ /\A\s*\z/;
return 1;
}
sub _resolve_entrypoint_from_manifest {
my ($entrypoint) = @_;
my $state = _state();
my $manifest_entrypoint = $state->{manifest}{entrypoint}{logical_path} // '';
if ($manifest_entrypoint ne '') {
my $candidate = File::Spec->catfile($state->{root}, 'code', split m{/}, $manifest_entrypoint);
return $candidate if -f $candidate;
}
my @unit_candidates = grep {
my $unit = $_;
my $unit_kind = $unit->{unit_kind} // '';
my $packaging = $unit->{packaging} // '';
($unit_kind // '') eq 'entrypoint' || ($packaging // '') =~ /\A(compiled|hybrid|residual)_(?:dispatch|cli_router|script)_pcu_v1\z/;
} @{ $state->{manifest}{code_units} // [] };
for my $unit (@unit_candidates) {
my $logical = $unit->{logical_path} // '';
next if $logical eq '';
my $candidate = File::Spec->catfile($state->{root}, 'code', split m{/}, $logical);
return $candidate if -f $candidate;
}
return;
}
sub _state {
return $STATE if $STATE;
my $manifest_path = $ENV{PAX_STANDALONE_MANIFEST_PATH} or die 'PAX_STANDALONE_MANIFEST_PATH not set';
open my $fh, '<', $manifest_path or die "cannot read $manifest_path: $!";
local $/;
my $manifest = _runtime_json_decode(<$fh>);
my $root = $ENV{PAX_STANDALONE_TMPDIR} or die 'PAX_STANDALONE_TMPDIR not set';
my $app_namespace = _normalize_namespace($manifest->{app}{namespace} // '');
if (!$app_namespace) {
$app_namespace = _normalize_namespace($manifest->{app}{compat}{namespace} // '');
}
my $legacy_namespace = _normalize_namespace($manifest->{app}{compat}{legacy_namespace} // '');
$legacy_namespace = $app_namespace if $legacy_namespace eq '';
my %by_region = map { ($_->{region_name} // '') => $_ } @{ $manifest->{native_dispatch} // [] };
my %compiled_packages;
my %compiled = map {
my $key = $_->{require_path} // '';
length($key) ? ($key => $_) : ()
} grep {
my $packaging = $_->{packaging} // '';
(($packaging eq 'compiled_pcu_v1') || ($packaging eq 'hybrid_compiled_pcu_v1'))
} @{ $manifest->{code_units} // [] };
for my $unit (@{ $manifest->{code_units} // [] }) {
lib/PAX/StandaloneRuntime.pm view on Meta::CPAN
my $class = _standalone_internal_cli_class() or return;
_load_package_by_module_name($class);
my $full = $class . '::helper_content';
return if !defined &{$full};
local $ENV{PAX_STANDALONE_EXECUTABLE} = '';
no strict 'refs';
my $content = &{$full}($name);
return if !defined $content || $content eq '';
return ($content, $name);
}
sub _standalone_embedded_asset_path {
my ($name) = @_;
return if !defined $name || $name eq '';
my $state = _state();
for my $asset (@{ $state->{manifest}{assets} // [] }) {
next if ref($asset) ne 'HASH';
my $logical = $asset->{logical_path} // '';
next if $logical eq '';
next if $logical ne $name && $logical !~ m{(?:^|/)\Q$name\E\z};
my $path = File::Spec->catfile($state->{root}, 'assets', split m{/}, $logical);
return $path if -f $path;
}
return;
}
sub _share_dist_private_cli_dir {
my ($dist_name) = @_;
return if !defined $dist_name || $dist_name eq '';
my $ok = eval {
require File::ShareDir;
1;
};
return if !$ok;
my $root = eval { File::ShareDir::dist_dir($dist_name) };
return if !$root || !-d $root;
my $candidate = File::Spec->catdir($root, 'private-cli');
return -d $candidate ? $candidate : undef;
}
sub _run_standalone_managed_helper {
my ($helper, @argv) = @_;
my ($helper_source, $helper_path) = _standalone_internal_cli_asset_content($helper);
die "standalone managed helper '$helper' is unavailable\n" if !defined $helper_source || $helper_source eq '';
my $self_path = _standalone_executable_path();
local $ENV{DEVELOPER_DASHBOARD_ENTRYPOINT} = $self_path if defined $self_path && $self_path ne '';
my ($source, $path, @helper_argv);
if ($helper eq '_dashboard-core' || _standalone_helper_delegates_to_dashboard_core($helper_source)) {
my ($core_source, $core_path) = _standalone_internal_cli_asset_content('_dashboard-core');
die "standalone managed helper core is unavailable\n" if !defined $core_source || $core_source eq '';
$source = $core_source;
$path = $core_path;
@helper_argv = @argv;
unshift @helper_argv, $helper if $helper ne '_dashboard-core';
}
else {
$source = $helper_source;
$path = $helper_path;
@helper_argv = @argv;
}
local @ARGV = @helper_argv;
local $0 = $path if defined $path && $path ne '';
my $wrapped = "package main;\n#line 1 \"$path\"\n" . $source;
my $rv = eval $wrapped;
die $@ if $@;
return 0 if !defined $rv;
return $rv;
}
sub _direct_standalone_helper_name_from_path {
my ($path) = @_;
return if !defined $path || $path eq '';
my $name = basename($path);
return if !defined $name || $name eq '';
return $name;
}
sub _standalone_helper_delegates_to_dashboard_core {
my ($source) = @_;
return 0 if !defined $source || $source eq '';
return 1 if $source =~ /_dashboard-core/
&& $source =~ /basename\(\$0\)/
&& $source =~ /exec\s+\{\s*\$\^X\s*\}\s+\$\^X,\s+\$core,\s+\$command,\s+\@ARGV;/s;
return 0;
}
sub _install_pending_wrappers {
my $state = _state();
for my $full (sort keys %{ $state->{by_region} }) {
next if $state->{wrapped}{$full};
next if !$full || ($full !~ /::/);
my $cv = _code_for($full) or next;
my $meta = $state->{by_region}{$full};
next if !$meta->{executable_logical_path};
my $original = $cv;
no strict 'refs';
no warnings 'redefine';
*{$full} = sub {
my @args = @_;
if (_eligible_i64_args(\@args)) {
my $guard = PAX::GuardManager->new(
epochs => $state->{manifest}{runtime_epochs} // {},
)->validate_or_deopt({
region_id => $meta->{region_id},
region_name => $meta->{region_name},
guards => $meta->{guards} // [],
deopt => $meta->{deopt} // {},
}, args => [ @args[0, 1] ], context => 'scalar');
if (($guard->{status} // '') eq 'native_allowed') {
my $probe = File::Spec->catfile($state->{root}, split m{/}, $meta->{executable_logical_path});
chmod 0700, $probe if -f $probe;
my $result = $state->{native_runner}->run_i64_binary(
path => $probe,
left => $args[0],
right => $args[1],
);
if (($result->{status} // '') eq 'ok' && defined $result->{value}) {
_log_native_hit($full);
return $result->{value};
}
( run in 0.764 second using v1.01-cache-2.11-cpan-8dfa8b56332 )