The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

use strict;
use Carp 'confess';
use JSON::PP ();
use File::Basename 'basename';
# Fields
sub script_name {
my $self = shift;
if (@_) {
$self->{script_name} = $_[0];
return $self;
}
else {
return $self->{script_name};
}
}
sub with_version {
my $self = shift;
if (@_) {
$self->{with_version} = $_[0];
return $self;
}
else {
return $self->{with_version};
}
}
sub excluded_class_names {
my $self = shift;
if (@_) {
$self->{excluded_class_names} = $_[0];
return $self;
}
else {
return $self->{excluded_class_names};
}
}
# Class Methods
sub new {
my $class = shift;
my $self = {
excluded_class_names => [],
@_,
};
return bless $self, ref $class || $class;
}
sub to_lines {
my ($class, $array) = @_;
my $lines = '';
for my $elem (@$array) {
$lines .= "$elem\x0A";
}
return $lines;
}
# Instance Methods
sub dump_resource_info {
my ($self) = @_;
my $script_name = $self->{script_name};
my $script_info = SPVM::Builder::ScriptInfo->new(script_name => $script_name);
my $class_names = [grep { $script_info->is_resource_loader($_) } @{$script_info->get_class_names}];
my $resource_info = "";
for my $class_name (@$class_names) {
my $config_file = $script_info->get_config_file($class_name);
$resource_info .= <<"EOS";
[$class_name]
# $config_file
# Loaded Resources:
EOS
for my $resource_name (@{$script_info->get_config($class_name)->get_resource_names}) {
$resource_info .= "# $resource_name\n";
}
my $config_content = $script_info->get_config_content($class_name);
$resource_info .= "$config_content\n";
}
return $resource_info;
}
sub to_class_infos {
my ($self) = @_;
my $script_name = $self->{script_name};
my $excluded_class_names = $self->{excluded_class_names};
my $script_info = SPVM::Builder::ScriptInfo->new(script_name => $script_name);
my $runtime = $script_info->runtime;
my $class_names = $script_info->get_class_names;
my $class_infos = [];
for my $class_name (sort @$class_names) {
my $skip = 0;
for my $excluded_class_name (@$excluded_class_names) {
if ($excluded_class_name =~ /[^\w:\*]/) {
Carp::confess("--exclude option in spvmdeps command contains invalid characters.");
}
my $excluded_class_name_re = $excluded_class_name;
$excluded_class_name_re =~ s/\*/.*/g;
$excluded_class_name_re = "^$excluded_class_name_re\$";
if ($class_name =~ /$excluded_class_name_re/) {
$skip = 1;
last;
}
}
next if $skip;
my $basic_type = $runtime->get_basic_type_by_name($class_name);
my $class_info = {};
$class_info->{class_name} = $class_name;
my $version_string = $basic_type->get_version_string;
my $basic_type_in_version_from = $basic_type->get_basic_type_in_version_from;
if (defined $version_string) {
$class_info->{version} = $version_string;
}
elsif ($basic_type_in_version_from) {
my $basic_type_name_in_version_from = $basic_type_in_version_from->get_name;
$class_info->{version_from} = $basic_type_name_in_version_from;
}
push @$class_infos, $class_info;
}
return $class_infos;
}
sub to_json {
my ($self) = @_;
my $script_name = $self->{script_name};
my $with_version = $self->{with_version};
my $script_info = SPVM::Builder::ScriptInfo->new(script_name => $script_name);
my $class_infos = $self->to_class_infos;
my $class_names = [];
my $json = "[\x0A";
for (my $i = 0; $i < @$class_infos; $i++) {
my $class_info = $class_infos->[$i];
my $class_name = $class_info->{class_name};
my $version = $class_info->{version};
my $version_from = $class_info->{version_from};
$json .= " {";
$json .= "\"class_name\":\"$class_name\"";
if ($with_version) {
if (defined $version) {
$json .= ",\"version\":\"$version\"";
}
elsif ($version_from) {
$json .= ",\"version_from\":\"$version_from\"";
}
}
$json .= "}";
if ($i < @$class_infos - 1) {
$json .= ",";
}
$json .= "\x0A";
}
$json .= "]\x0A";
return $json;
}
sub to_class_names {
my ($self) = @_;
my $script_name = $self->{script_name};
my $with_version = $self->{with_version};
my $script_info = SPVM::Builder::ScriptInfo->new(script_name => $script_name);
my $class_infos = $self->to_class_infos;
my $class_names = [];
for my $class_info (@$class_infos) {
my $class_name_only = $class_info->{class_name};
my $version = $class_info->{version};
my $version_from = $class_info->{version_from};
my $class_name = "$class_name_only";
if ($with_version) {
if (defined $version) {
$class_name .= " $version";
}
elsif ($version_from) {
$class_name .= " (version_from $version_from)";
}
}
push @$class_names, $class_name;
}
return $class_names;
}
sub to_cpanm_commands {
my ($self) = @_;
my $script_name = $self->{script_name};
my $with_version = $self->{with_version};
my $script_info = SPVM::Builder::ScriptInfo->new(script_name => $script_name);
my $class_infos = $self->to_class_infos;
my $cpanm_commands = [];
for my $class_info (@$class_infos) {
my $class_name = $class_info->{class_name};
my $version = $class_info->{version};
if (defined $version) {
my $cpanm_command = "cpanm ";
if ($class_name eq "SPVM") {
$cpanm_command .= "SPVM";
}
else {
$cpanm_command .= "SPVM::$class_name";
}
if ($with_version) {
if (defined $version) {
$cpanm_command .= "\@$version";
}
}
push @$cpanm_commands, $cpanm_command;
}
}
return $cpanm_commands;
}
sub to_cpanfile_commands {
my ($self) = @_;
my $script_name = $self->{script_name};
my $with_version = $self->{with_version};
my $script_info = SPVM::Builder::ScriptInfo->new(script_name => $script_name);
my $class_infos = $self->to_class_infos;
my $cpanfile_commands = [];
for my $class_info (@$class_infos) {
my $class_name = $class_info->{class_name};
my $version = $class_info->{version};
if (defined $version) {
my $cpanfile_command = "requires ";
if ($class_name eq "SPVM") {
$cpanfile_command .= "'SPVM'";
}
else {
$cpanfile_command .= "'SPVM::$class_name'";
}
if ($with_version) {
if (defined $version) {
$cpanfile_command .= ", '== $version'";
}
}
$cpanfile_command .= ";";
push @$cpanfile_commands, $cpanfile_command;
}
}
return $cpanfile_commands;
}
1;
=head1 Name
SPVM::Builder::DependencyAnalyzer - Utility for Analyzing Class Dependencies
=head1 Copyright & License
Copyright (c) 2023 Yuki Kimoto
MIT License