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

use strict;
use Config;
use Carp 'confess';
use File::Basename 'dirname', 'fileparse';
# Fields
sub class_name {
my $self = shift;
if (@_) {
$self->{class_name} = $_[0];
return $self;
}
else {
return $self->{class_name};
}
}
sub file {
my $self = shift;
if (@_) {
$self->{file} = $_[0];
return $self;
}
else {
return $self->{file};
}
}
sub file_optional {
my $self = shift;
if (@_) {
$self->{file_optional} = $_[0];
return $self;
}
else {
return $self->{file_optional};
}
}
sub ext {
my $self = shift;
if (@_) {
$self->{ext} = $_[0];
return $self;
}
else {
return $self->{ext};
}
}
sub quiet {
my $self = shift;
if (@_) {
$self->{quiet} = $_[0];
return $self;
}
else {
return $self->{quiet};
}
}
sub force {
my $self = shift;
if (@_) {
$self->{force} = $_[0];
return $self;
}
else {
return $self->{force};
}
}
sub cc {
my $self = shift;
if (@_) {
$self->{cc} = $_[0];
return $self;
}
else {
return $self->{cc};
}
}
sub ccflags {
my $self = shift;
if (@_) {
$self->{ccflags} = $_[0];
return $self;
}
else {
return $self->{ccflags};
}
}
sub dynamic_lib_ccflags {
my $self = shift;
if (@_) {
$self->{dynamic_lib_ccflags} = $_[0];
return $self;
}
else {
return $self->{dynamic_lib_ccflags};
}
}
sub thread_ccflags {
my $self = shift;
if (@_) {
$self->{thread_ccflags} = $_[0];
return $self;
}
else {
return $self->{thread_ccflags};
}
}
sub mingw_ccflags {
my $self = shift;
if (@_) {
$self->{mingw_ccflags} = $_[0];
return $self;
}
else {
return $self->{mingw_ccflags};
}
}
sub std {
my $self = shift;
if (@_) {
$self->{std} = $_[0];
return $self;
}
else {
return $self->{std};
}
}
sub optimize {
my $self = shift;
if (@_) {
$self->{optimize} = $_[0];
return $self;
}
else {
return $self->{optimize};
}
}
sub include_dirs {
my $self = shift;
if (@_) {
$self->{include_dirs} = $_[0];
return $self;
}
else {
return $self->{include_dirs};
}
}
sub spvm_core_include_dir {
my $self = shift;
if (@_) {
$self->{spvm_core_include_dir} = $_[0];
return $self;
}
else {
return $self->{spvm_core_include_dir};
}
}
sub native_include_dir {
my $self = shift;
if (@_) {
$self->{native_include_dir} = $_[0];
return $self;
}
else {
return $self->{native_include_dir};
}
}
sub native_src_dir {
my $self = shift;
if (@_) {
$self->{native_src_dir} = $_[0];
return $self;
}
else {
return $self->{native_src_dir};
}
}
sub source_files {
my $self = shift;
if (@_) {
$self->{source_files} = $_[0];
return $self;
}
else {
return $self->{source_files};
}
}
sub after_create_compile_info_cbs {
my $self = shift;
if (@_) {
$self->{after_create_compile_info_cbs} = $_[0];
return $self;
}
else {
return $self->{after_create_compile_info_cbs};
}
}
sub before_compile_cbs {
my $self = shift;
if (@_) {
$self->{before_compile_cbs} = $_[0];
return $self;
}
else {
return $self->{before_compile_cbs};
}
}
sub ld {
my $self = shift;
if (@_) {
$self->{ld} = $_[0];
return $self;
}
else {
return $self->{ld};
}
}
sub ldflags {
my $self = shift;
if (@_) {
$self->{ldflags} = $_[0];
return $self;
}
else {
return $self->{ldflags};
}
}
sub dynamic_lib_ldflags {
my $self = shift;
if (@_) {
$self->{dynamic_lib_ldflags} = $_[0];
return $self;
}
else {
return $self->{dynamic_lib_ldflags};
}
}
sub thread_ldflags {
my $self = shift;
if (@_) {
$self->{thread_ldflags} = $_[0];
return $self;
}
else {
return $self->{thread_ldflags};
}
}
sub static_lib_ldflag {
my $self = shift;
if (@_) {
$self->{static_lib_ldflag} = $_[0];
return $self;
}
else {
return $self->{static_lib_ldflag};
}
}
sub ld_optimize {
my $self = shift;
if (@_) {
$self->{ld_optimize} = $_[0];
return $self;
}
else {
return $self->{ld_optimize};
}
}
sub lib_dirs {
my $self = shift;
if (@_) {
$self->{lib_dirs} = $_[0];
return $self;
}
else {
return $self->{lib_dirs};
}
}
sub libs {
my $self = shift;
if (@_) {
$self->{libs} = $_[0];
return $self;
}
else {
return $self->{libs};
}
}
sub after_create_link_info_cbs {
my $self = shift;
if (@_) {
$self->{after_create_link_info_cbs} = $_[0];
return $self;
}
else {
return $self->{after_create_link_info_cbs};
}
}
sub before_link_cbs {
my $self = shift;
if (@_) {
$self->{before_link_cbs} = $_[0];
return $self;
}
else {
return $self->{before_link_cbs};
}
}
sub output_type {
my $self = shift;
if (@_) {
$self->{output_type} = $_[0];
return $self;
}
else {
return $self->{output_type};
}
}
sub resource_loader_config {
my $self = shift;
if (@_) {
$self->{resource_loader_config} = $_[0];
return $self;
}
else {
return $self->{resource_loader_config};
}
}
sub category {
my $self = shift;
if (@_) {
$self->{category} = $_[0];
return $self;
}
else {
return $self->{category};
}
}
sub config_exe {
my $self = shift;
if (@_) {
$self->{config_exe} = $_[0];
return $self;
}
else {
return $self->{config_exe};
}
}
sub is_jit {
my $self = shift;
if (@_) {
$self->{is_jit} = $_[0];
return $self;
}
else {
return $self->{is_jit};
}
}
sub cc_input_dir {
my $self = shift;
if (@_) {
$self->{cc_input_dir} = $_[0];
return $self;
}
else {
return $self->{cc_input_dir};
}
}
sub cc_output_dir {
my $self = shift;
if (@_) {
$self->{cc_output_dir} = $_[0];
return $self;
}
else {
return $self->{cc_output_dir};
}
}
sub output_dir {
my $self = shift;
if (@_) {
$self->{output_dir} = $_[0];
return $self;
}
else {
return $self->{output_dir};
}
}
sub output_file {
my $self = shift;
if (@_) {
$self->{output_file} = $_[0];
return $self;
}
else {
return $self->{output_file};
}
}
sub is_resource {
my $self = shift;
if (@_) {
$self->{is_resource} = $_[0];
return $self;
}
else {
return $self->{is_resource};
}
}
sub mode {
my $self = shift;
if (@_) {
$self->{mode} = $_[0];
return $self;
}
else {
return $self->{mode};
}
}
# Class Methods
sub new {
my $class = shift;
my $self = {@_};
bless $self, ref $class || $class;
my $file_optional = $self->file_optional;
my $file = $self->file;
if (!$file_optional && !defined $file) {
confess("The \"file\" field must be defined");
}
# cc
unless (defined $self->{cc}) {
$self->cc($Config{cc});
}
# ccflags
unless (defined $self->{ccflags}) {
$self->ccflags([]);
}
# dynamic_lib_ccflags
unless (defined $self->{dynamic_lib_ccflags}) {
if ($^O eq 'MSWin32') {
$self->dynamic_lib_ccflags([]);
}
else {
$self->dynamic_lib_ccflags(['-fPIC']);
}
}
# thread_ccflags
unless (defined $self->{thread_ccflags}) {
if ($^O eq 'MSWin32') {
$self->thread_ccflags([]);
}
else {
$self->thread_ccflags(['-pthread']);
}
}
# mingw_ccflags
unless (defined $self->{mingw_ccflags}) {
if ($^O eq 'MSWin32') {
$self->mingw_ccflags(['-D__USE_MINGW_ANSI_STDIO']);
}
else {
$self->mingw_ccflags([]);
}
}
# optimize
unless (defined $self->{optimize}) {
$self->optimize('-O3');
}
# include_dirs
unless (defined $self->{include_dirs}) {
$self->include_dirs([]);
}
# spvm_core_include_dir
unless (defined $self->spvm_core_include_dir) {
my $builder_dir = SPVM::Builder::Util::get_builder_dir();
my $spvm_core_include_dir = "$builder_dir/include";
$self->spvm_core_include_dir($spvm_core_include_dir);
}
# native_include_dir
unless (defined $self->native_include_dir) {
if (defined $file) {
my $native_dir = $self->_remove_ext_from_config_file($file);
$native_dir .= '.native';
my $native_include_dir = "$native_dir/include";
$self->native_include_dir($native_include_dir);
}
}
# native_src_dir
unless (defined $self->native_src_dir) {
if (defined $file) {
my $native_dir = $self->_remove_ext_from_config_file($file);
$native_dir .= '.native';
my $native_src_dir = "$native_dir/src";
$self->native_src_dir($native_src_dir);
}
}
# source_files
unless (defined $self->{source_files}) {
$self->source_files([]);
}
# after_create_compile_info_cbs
unless (defined $self->{after_create_compile_info_cbs}) {
$self->after_create_compile_info_cbs([]);
}
# before_compile_cbs
unless (defined $self->{before_compile_cbs}) {
$self->before_compile_cbs([]);
}
# ld
unless (defined $self->{ld}) {
$self->ld($Config{ld});
}
# ldflags
unless (defined $self->{ldflags}) {
$self->ldflags([]);
}
# dynamic_lib_ldflags
unless (defined $self->{dynamic_lib_ldflags}) {
if ($^O eq 'MSWin32') {
$self->dynamic_lib_ldflags(['-mdll', '-s']);
}
else {
$self->dynamic_lib_ldflags(['-shared', '-pthread']);
}
}
# thread_ldflags
unless (defined $self->{thread_ldflags}) {
if ($^O eq 'MSWin32') {
$self->thread_ldflags([]);
}
else {
$self->thread_ldflags(['-pthread']);
}
}
# static_lib_ldflag
unless (defined $self->{static_lib_ldflag}) {
my $begin = '-Wl,-Bstatic';
my $end = '-Wl,-Bdynamic';
$self->static_lib_ldflag([$begin, $end]);
}
# ld_optimize
unless (defined $self->{ld_optimize}) {
$self->ld_optimize('-O2');
}
# lib_dirs
unless (defined $self->{lib_dirs}) {
$self->lib_dirs([]);
}
# libs
unless (defined $self->{libs}) {
$self->libs([]);
}
# after_create_link_info_cbs
unless (defined $self->{after_create_link_info_cbs}) {
$self->after_create_link_info_cbs([]);
}
# before_link_cbs
unless (defined $self->{before_link_cbs}) {
$self->before_link_cbs([]);
}
# output_type
unless (defined $self->output_type) {
$self->output_type('dynamic_lib');
}
# category
unless (defined $self->{category}) {
$self->category('native');
}
unless (defined $self->{_loaded_config_files}) {
$self->{_loaded_config_files} = [];
}
# resources
unless (defined $self->{resources}) {
$self->{resources} = {};
}
return $self;
}
sub new_c {
my $class = shift;
my $self = $class->new(@_);
$self->ext('c');
return $self;
}
sub new_c99 {
my $class = shift;
my $self = $class->new_c(@_);
# C99
$self->std('c99');
return $self;
}
sub new_c11 {
my $class = shift;
my $self = $class->new_c(@_);
# C11
$self->std('c11');
return $self;
}
sub new_gnu99 {
my $class = shift;
my $self = $class->new_c(@_);
# GNU C99
$self->std('gnu99');
return $self;
}
sub new_gnu11 {
my $class = shift;
my $self = $class->new_c(@_);
# GNU C11
$self->std('gnu11');
return $self;
}
sub new_cpp {
my $class = shift;
my $self = $class->new(@_);
# The compiler
# [Memo]Free BSD don't have g++ in the environment clang++ exists.
# [Memo]"Clang" or "clang" is assumed.
my $config_gcc_version = $Config{gccversion};
if ($config_gcc_version =~ /\bclang\b/i) {
$self->cc('clang++');
$self->ld('clang++');
}
else {
$self->cc('g++');
$self->ld('g++');
}
$self->ext('cpp');
return $self;
}
sub new_cpp11 {
my $class = shift;
my $self = $class->new_cpp(@_);
# C++11
$self->std('c++11');
return $self;
}
sub new_cpp14 {
my $class = shift;
my $self = $class->new_cpp(@_);
# C++14
$self->std('c++14');
return $self;
}
sub new_cpp17 {
my $class = shift;
my $self = $class->new_cpp(@_);
# C++17
$self->std('c++17');
return $self;
}
# Instance Methods
sub add_ccflag {
my ($self, @ccflags) = @_;
push @{$self->{ccflags}}, @ccflags;
}
sub add_include_dir {
my ($self, @include_dirs) = @_;
push @{$self->{include_dirs}}, @include_dirs;
}
sub add_ldflag {
my ($self, @ldflags) = @_;
push @{$self->{ldflags}}, @ldflags;
}
sub add_lib_dir {
my ($self, @lib_dirs) = @_;
push @{$self->{lib_dirs}}, @lib_dirs;
}
sub add_lib {
my ($self, @libs) = @_;
push @{$self->{libs}}, @libs;
}
sub add_lib_abs {
my ($self, @libs) = @_;
$self->_add_lib_info({is_abs => 1}, @libs);
}
sub add_static_lib_abs {
my ($self, @libs) = @_;
$self->_add_lib_info({is_static => 1, is_abs => 1}, @libs);
}
sub add_static_lib {
my ($self, @libs) = @_;
$self->_add_lib_info({is_static => 1}, @libs);
}
sub _add_lib_info {
my ($self, $options, @libs) = @_;
my @lib_infos;
for my $lib (@libs) {
my $lib_info;
if (ref $lib eq 'SPVM::Builder::LibInfo') {
$lib_info = $lib;
}
else {
my $lib_name = $lib;
$lib_info = SPVM::Builder::LibInfo->new(config => $self, name => $lib_name);
}
$lib_info->is_static($options->{is_static});
$lib_info->is_abs($options->{is_abs});
push @lib_infos, $lib_info;
}
$self->add_lib(@lib_infos);
}
sub add_source_file {
my ($self, @source_files) = @_;
push @{$self->{source_files}}, @source_files;
}
sub add_after_create_compile_info_cb {
my ($self, @after_create_compile_info_cbs) = @_;
push @{$self->{after_create_compile_info_cbs}}, @after_create_compile_info_cbs;
}
sub add_before_compile_cb {
my ($self, @before_compile_cbs) = @_;
push @{$self->{before_compile_cbs}}, @before_compile_cbs;
}
sub add_after_create_link_info_cb {
my ($self, @after_create_link_info_cbs) = @_;
push @{$self->{after_create_link_info_cbs}}, @after_create_link_info_cbs;
}
sub add_before_link_cb {
my ($self, @before_link_cbs) = @_;
push @{$self->{before_link_cbs}}, @before_link_cbs;
}
sub load_config {
my ($self, $config_file, $argv) = @_;
unless (-f $config_file) {
confess("The config file \"$config_file\" must exist");
}
unless (defined $argv) {
confess("The arguments \$argv must be defined.");
}
my $config;
{
local @ARGV = @$argv;
$config = do File::Spec->rel2abs($config_file);
}
if ($@) {
confess("The config file \"$config_file\" can't be parsed: $@");
}
unless (defined $config && $config->isa('SPVM::Builder::Config')) {
confess("The config file must be an SPVM::Builder::Config object");
}
push @{$config->get_loaded_config_files}, $config_file;
return $config;
}
sub load_mode_config {
my ($self, $config_file, $mode, $argv) = @_;
my $mode_config_file = $self->_remove_ext_from_config_file($config_file);
if (defined $mode) {
$mode_config_file .= ".$mode";
}
$mode_config_file .= ".config";
unless (-f $mode_config_file) {
confess("Can't find the config file \"$mode_config_file\"");
}
my $config = $self->load_config($mode_config_file, $argv);
if (defined $mode) {
$config->mode($mode);
}
return $config;
}
sub load_base_config {
my ($self, $config_file, $args) = @_;
my $config = $self->load_mode_config($config_file, undef, $args);
return $config;
}
sub get_loaded_config_files {
my $self = shift;
return $self->{_loaded_config_files};
}
sub use_resource {
my ($self, @args) = @_;
my $first_arg;
unless (@args % 2 == 0) {
$first_arg = shift @args;
}
my $resource;
if (ref $first_arg) {
$resource = $first_arg;
}
else {
my $class_name = $first_arg;
my %args = @args;
if (exists $args{class_name}) {
$class_name = delete $args{class_name};
}
$resource = SPVM::Builder::Resource->new(class_name => $class_name, %args);
}
my $resource_class_name = $resource->class_name;
my $resource_mode = $resource->mode;
my $resource_argv = $resource->argv;
my $ext = defined $resource_mode ? "$resource_mode.config" : 'config';
my $config_file_base = SPVM::Builder::Util::convert_class_name_to_rel_file($resource_class_name, $ext);
my $config_file = SPVM::Builder::Util::search_config_file($resource_class_name, $resource_mode);
unless (defined $config_file) {
my $config_rel_file = SPVM::Builder::Util::convert_class_name_to_rel_file($resource_class_name, 'config');
confess("A config file \"$config_rel_file\" is not found in (@INC)");
}
my $config = $self->load_config($config_file, $resource_argv);
$config->file($config_file);
if (defined $resource_mode) {
$config->mode($resource_mode);
}
$resource->config($config);
my $index = keys %{$self->{resources}};
$self->{resources}->{$resource_class_name} = {resource => $resource, index => $index};
return $resource;
}
sub get_resource {
my ($self, $resource_class_name) = @_;
unless (defined $self->{resources}{$resource_class_name}) {
return;
}
my $resource = $self->{resources}{$resource_class_name}{resource};
return $resource;
}
sub get_resource_names {
my ($self) = @_;
my @resource_names = sort { $self->{resources}{$a}{index} <=> $self->{resources}{$b}{index} } keys %{$self->{resources}};
return \@resource_names;
}
sub clone {
my ($self) = @_;
my $clone = bless {}, ref $self;
for my $name (keys %$self) {
my $value = $self->{$name};
if (ref $value eq 'ARRAY') {
$clone->{$name} = [@$value];
}
elsif (ref $value eq 'HASH') {
$clone->{$name} = {%$value};
}
else {
$clone->{$name} = $value;
}
}
return $clone;
}
sub _remove_ext_from_config_file {
my ($self, $config_file) = @_;
my ($config_base_name, $config_dir) = fileparse $config_file;
$config_base_name =~ s/(\.[^\.]+)?\.config$//;
my $config_file_without_ext = "$config_dir$config_base_name";
return $config_file_without_ext;
}
1;
=head1 Name
SPVM::Builder::Config - Config for Compiling and Linking Native Classes
=head1 Description
The SPVM::Builder::Config class has methods to get and set config for compiling and linking L<native classes|SPVM::Document::NativeClass>.
=head1 Usage
# Create a config
my $config = SPVM::Builder::Config->new(file => __FILE__);
# GNU C99
my $config = SPVM::Builder::Config->new_gnu99(file => __FILE__);
# C99
my $config = SPVM::Builder::Config->new_c99(file => __FILE__);
# C++
my $config = SPVM::Builder::Config->new_cpp(file => __FILE__);
# C++11
my $config = SPVM::Builder::Config->new_cpp11(file => __FILE__);
# C++17
my $config = SPVM::Builder::Config->new_cpp17(file => __FILE__);
# Optimize
$config->optimize("-O2");
# Optimize with debug mode
$config->optimize("-O0 -g");
# Add ccflags
$config->add_ccflag("-DFOO");
# Add source files
$config->add_source_file("foo.c", "bar.c", "baz/baz.c");
# Add libraries
$config->add_lib("gdi32", "d2d1", "Dwrite");
# Add ldflags
$config->add_ldflag("-pthread");
# Use resource
$config->use_resource("Resource::MyResource");
=head1 Fields
=head2 ext
my $ext = $config->ext;
$config->ext($ext);
Gets and sets C<ext> field, the extension of a native class.
Examples:
# MyClass.c
$config->ext('c');
# MyClass.cpp
$config->ext('cpp');
# MyClass.cc
$config->ext('cc');
# MyClass.cu
$config->ext('cu');
# MyClass.m
$config->ext('m');
=head2 cc
my $cc = $config->cc;
$config->cc($cc);
Gets and sets C<cc> field, a compiler name.
Examples:
# gcc
$config->cc('gcc');
# g++ for C++
$config->cc('g++');
# nvcc for CUDA/GUP
$config->cc('nvcc');
# cc that compiled this Perl
use Config;
$config->cc($Config{cc});
=head2 include_dirs
my $include_dirs = $config->include_dirs;
$config->include_dirs($include_dirs);
Gets and sets C<include_dirs> field, an array reference containing header file search directories.
The values of this field are converted to C<-I> options when the arguments of the compiler L</"cc"> are created.
# -I /path1 -I /path2
$config->include_dirs(['/path1', '/path2']);
=head2 spvm_core_include_dir
my $spvm_core_include_dir = $config->spvm_core_include_dir;
$config->spvm_core_include_dir($spvm_core_include_dir);
Gets and sets C<spvm_core_include_dir> field, an SPVM core header file search directory.
The value of this field is converted to C<-I> option when the arguments of the compiler L</"cc"> are created.
This field is automatically set and users nomally do not change it.
=head2 native_include_dir
my $native_include_dir = $config->native_include_dir;
$config->native_include_dir($native_include_dir);
Gets and sets C<native_include_dir> field, a L<native header file|SPVM::Document::NativeClass/"Native Header Files"> search directory.
The value of this field is converted to C<-I> option when the arguments of the compiler L</"cc"> are created.
This field is automatically set and users nomally do not change it.
=head2 native_src_dir
my $native_src_dir = $config->native_src_dir;
$config->native_src_dir($native_src_dir);
Gets and sets C<native_src_dir> field, a L<native source file|SPVM::Document::NativeClass/"Native Source Files"> search directory.
This field is automatically set and users nomally do not change it.
=head2 ccflags
my $ccflags = $config->ccflags;
$config->ccflags($ccflags);
Gets and sets C<ccflags> field, an array reference containing arugments of the compiler L</"cc">.
=head2 dynamic_lib_ccflags
my $dynamic_lib_ccflags = $config->dynamic_lib_ccflags;
$config->dynamic_lib_ccflags($dynamic_lib_ccflags);
Gets and sets C<dynamic_lib_ccflags> field, an array reference containing arugments of the compiler L</"cc"> for dynamic linking.
This field is automatically set and users nomally do not change it.
=head2 thread_ccflags
my $thread_ccflags = $config->thread_ccflags;
$config->thread_ccflags($thread_ccflags);
Gets and sets C<thread_ccflags> field, an array reference containing arugments of the compiler L</"cc"> for threads.
This field is automatically set and users nomally do not change it.
=head2 mingw_ccflags
my $mingw_ccflags = $config->mingw_ccflags;
$config->mingw_ccflags($mingw_ccflags);
Gets and sets C<mingw_ccflags> field, an array reference containing arugments of the compiler L</"cc"> for MinGW.
This field is automatically set and users nomally do not change it.
=head2 std
my $std = $config->std;
$config->std($std);
Gets and sets C<std> field, a language standard.
This field is converted to C<-std> option when the arguments of the compiler L</"cc"> are created.
Examples:
# -std=c99
$config->std('c99');
# -std=gnu99
$config->std('gnu99');
# -std=cpp
$config->std('cpp');
# -std=cpp11
$config->std('cpp11');
# -std=cpp17
$config->std('cpp17');
=head2 optimize
my $optimize = $config->optimize;
$config->optimize($optimize);
Gets and sets C<optimize> field, an arugment of the compiler L</"cc"> for optimization.
Examples:
$config->optimize('-O3');
$config->optimize('-O2');
$config->optimize('-g3 -O0');
=head2 source_files
my $source_files = $config->source_files;
$config->source_files($source_files);
Gets and sets C<source_files> field, an array reference containing relative paths of L<native source file|SPVM::Document::NativeClass/"Native Source Files"> file from L</"native_src_dir"> field.
=head2 after_create_compile_info_cbs
my $after_create_compile_info_cbs = $config->after_create_compile_info_cbs;
$config->after_create_compile_info_cbs($after_create_compile_info_cbs);
Gets and sets C<after_create_compile_info_cbs> field, an array reference containing callbacks called just after creating compilation information.
These callbacks are executed even if no object file was generated.
The 1th argument of the callback is an L<SPVM::Builder::Config> object.
The 2th argument of the callback is an L<SPVM::Builder::CompileInfo> object.
=head2 before_compile_cbs
my $before_compile_cbs = $config->before_compile_cbs;
$config->before_compile_cbs($before_compile_cbs);
Gets and sets C<before_compile_cbs> field, an array reference containing callbacks called just before the compile command L</"cc"> is executed.
These callbacks are executed only if an object file is actually generated.
The 1th argument of the callback is an L<SPVM::Builder::Config> object.
The 2th argument of the callback is an L<SPVM::Builder::CompileInfo> object.
=head2 ld
my $ld = $config->ld;
$config->ld($ld);
Gets and sets C<ld> field, a linker name.
Examples:
$config->ld('gcc');
$config->ld('g++');
=head2 lib_dirs
my $lib_dirs = $config->lib_dirs;
$config->lib_dirs($lib_dirs);
Gets and sets C<lib_dirs> field, an array reference containing library search directories.
The values of this field are converted to C<-L> options when the arguments of the linker L</"ld"> are created.
# -L /path1 -L /path2
$config->lib_dirs(['/path1', '/path2']);
=head2 libs
my $libs = $config->libs;
$config->libs($libs);
Gets and sets C<libs> field, an array reference containing library names such as C<z>, and C<png> or L<SPVM::Builder::LibInfo> objects.
The values of this field are converted to C<-l> options when the arguments of the linker L</"ld"> are created.
See L</"Library Path Resolution"> about resolving library paths.
Examples:
# -l libz -l libpng
$config->libs(['z', 'png']);
=head2 ldflags
my ldflags = $config->ldflags;
$config->ldflags(ldflags);
Gets and sets C<ldflags> field, an array reference containing arguments of the linker L</"ld">.
=head2 dynamic_lib_ldflags
my dynamic_lib_ldflags = $config->dynamic_lib_ldflags;
$config->dynamic_lib_ldflags(dynamic_lib_ldflags);
Gets and sets C<dynamic_lib_ldflags> field, an array reference containing arguments of the linker L</"ld"> for dynamic libraries.
This field is automatically set and users nomally do not change it.
=head2 thread_ldflags
my thread_ldflags = $config->thread_ldflags;
$config->thread_ldflags(thread_ldflags);
Gets and sets C<thread_ldflags> field, an array reference containing arguments of the linker L</"ld"> for threads.
This field is automatically set and users nomally do not change it.
=head2 static_lib_ldflag
my static_lib_ldflag = $config->static_lib_ldflag;
$config->static_lib_ldflag(static_lib_ldflag);
Gets and sets C<static_lib_ldflag> field, an array reference containing a pair of arguments to start statically linking and end it.
The library name added by the L</"add_static_lib"> are surrounded by the values of the pair.
# -Wl,-Bstatic -llibfoo -Wl,-Bdynamic
$config->static_lib_ldflag(['-Wl,-Bstatic', '-Wl,-Bdynamic']);
$config->add_static_lib('foo');
This field is automatically set and users nomally do not change it.
This field only works correctly in Linux/Unix.
Mac does not support these options. If you want to search a static library, create a new library search directory, copy a static library to there, and add the new library search directory.
# /path_for_static_lib/libz.a
$config->add_lib_dir('/path_for_static_lib');
$config->add_lib('z');
MinGW on Windows supports these options, but instead of linking statically, it links dynamically with absolute paths. This is usually not the intended behavior. If you want to do static linking on Windows, you need to use C<-static> option.
=head2 ld_optimize
my $ld_optimize = $config->ld_optimize;
$config->ld_optimize($ld_optimize);
Gets and sets C<ld_optimize> field, an argument of the linker L</"ld"> for optimization.
Examples:
$config->ld_optimize("-O3");
=head2 after_create_link_info_cbs
my $after_create_link_info_cbs = $config->after_create_link_info_cbs;
$config->after_create_link_info_cbs($after_create_link_info_cbs);
Gets and sets C<after_create_link_info_cbs> field, an array reference containing callbacks called just after creating link information.
These callbacks are executed even if no dynamic link library file was generated.
The 1th argument of the callback is an L<SPVM::Builder::Config> object.
The 2th argument of the callback is an L<SPVM::Builder::LinkInfo> object.
=head2 before_link_cbs
my $before_link_cbs = $config->before_link_cbs;
$config->before_link_cbs($before_link_cbs);
Gets and sets C<before_link_cbs> field, an array reference containing callbacks called just before the link command L</"ld"> is executed.
These callbacks are executed only if a dynamic link library is actually generated.
The 1th argument of the callback is an L<SPVM::Builder::Config> object.
The 2th argument of the callback is an L<SPVM::Builder::LinkInfo> object.
=head2 force
my $force = $config->force;
$config->force($force);
Gets and sets C<force> field.
If this field is a true value, the compilation and linking are forced.
If this field is a false value except for undef, the compilation and linking are performed following the rule of the L<dependency resolution|SPVM::Document::NativeClass/"Dependency Resolution">.
If this field is undef, this config does not specify whether the compilation and linking are perfomed.
=head2 quiet
my $quiet = $config->quiet;
$config->quiet($quiet);
Gets and sets C<quiet> field.
If this field is a true value, the messages from the compiler and the linker are output to C<stderr>.
If this field is a false value except for undef, the messages from the compiler and the linker are not output.
If this field is undef, this config does specify whether the messages from the compiler and the linker are output.
=head2 class_name
my $class_name = $config->class_name;
$config->class_name($class_name);
Gets and sets C<class_name> field, the name of the class configured by this config.
This field is automatically set and users nomally do not change it.
=head2 file
my $file = $config->file;
$config->file($file);
Gets and sets C<file> field, the file path of this config.
This field is set by L</"new"> method and users nomally do not change it.
=head2 file_optional
my $file_optional = $config->file_optional;
$config->file_optional($file_optional);
Gets and sets C<file_optional> field.
If this field is a true value, even if the L<file|/"file"> field is not given to L</"new"> method, the exception is not thrown.
=head2 output_type
my $output_type = $config->output_type;
$config->output_type($output_type);
Gets and sets C<output_type> field, a type of the output file L</"output_file"> generated by the linker L</"ld">.
If thie field is C<dynamic_lib>, the output file is a dynamic link library.
If thie field is C<static_lib>, the output file is a static link library.
If thie field is C<exe>, the output file is an executable file.
This field is automatically set and users nomally do not change it.
=head2 resource_loader_config
my $resource_loader_config = $config->resource_loader_config;
$config->resource_loader_config($resource_loader_config);
Gets and sets C<resource_loader_config> field, the config file of the class that loaded a resource by L</"use_resource"> method.
This field is automatically set and users nomally do not change it.
=head2 category
my $category = $config->category;
$config->category($category);
Gets and sets C<category> field.
If this field is C<precompile>, this config is for precompilation,
If this field is C<native>, this config is for a native class.
This field is automatically set and users nomally do not change it.
=head2 config_exe
my $config_exe = $config->config_exe;
$config->config_exe($config_exe);
Gets and sets C<config_exe> field.
If L<spvmcc> command generates an excutable file, this field is set to an L<SPVM::Builder::Config::Exe> object.
This field is automatically set and users nomally do not change it.
=head2 cc_input_dir
my $cc_input_dir = $config->cc_input_dir;
$config->cc_input_dir($cc_input_dir);
Gets and sets C<cc_input_dir> field, an input directory for the compiler L</"cc">.
This field is automatically set and users nomally do not change it.
=head2 cc_output_dir
my $cc_output_dir = $config->cc_output_dir;
$config->cc_output_dir($cc_output_dir);
Gets and sets C<cc_output_dir> field, an output directory for the compiler L</"cc">.
This field is automatically set and users nomally do not change it.
=head2 output_dir
my $output_dir = $config->output_dir;
$config->output_dir($output_dir);
Gets and sets C<output_dir> field, an output directory for the linker L</"ld">.
This field is automatically set and users nomally do not change it.
=head2 output_file
my $output_file = $config->output_file;
$config->output_file($output_file);
Gets and sets C<output_file> field. A path of a dinamic link library or an executable file generated by the linker L</"ld">.
This field is automatically set and users nomally do not change it.
=head2 is_resource
my $is_resource = $config->is_resource;
$config->is_resource($is_resource);
Gets and sets C<is_resource> field.
If this field is true, this config is for a L<resource|SPVM::Document::Resource> class.
=head2 mode
my $mode = $config->mode;
$config->mode($mode);
Gets and sets C<mode> field.
=head1 Class Methods
=head2 new
my $config = SPVM::Builder::Config->new(%fields);
Creates a new C<SPVM::Builder::Config> object with L<fields|/"Fields">, and returns it.
The C<file> field must be defined.
Field Default Values:
=over 2
=item * L</"cc">
The C<$Config{cc}> of L<Config> module.
=item * L</"ccflags">
[]
=item * L</"dynamic_lib_ccflags">
Windows:
[]
Other OSs:
["-fPIC"]
=item * L</"thread_ccflags">
Windows:
[]
Other OSs:
["-pthread"]
=item * L</"mingw_ccflags">
Windows:
['-D__USE_MINGW_ANSI_STDIO']
Other OSs:
[]
=item * L</"optimize">
"-O3"
=item * L</"include_dirs">
[]
=item * L</"spvm_core_include_dir">
The SPVM core header file search directory.
=item * L</"native_include_dir">
The directory described in L<SPVM::Document::NativeClass/"Native Header Files">.
Examples:
MyClass.naitve/include
=item * L</"native_src_dir">
The directory described in L<SPVM::Document::NativeClass/"Native Source Files">.
Examples:
MyClass.naitve/src
=item * L</"source_files">
[]
=item * L</"after_create_compile_info_cbs">
[]
=item * L</"before_compile_cbs">
[]
=item * L</"ld">
The C<$Config{ld}> of L<Config> module.
=item * L</"ldflags">
[]
=item * L</"dynamic_lib_ldflags">
Windows:
["-mdll", "-s"]
Other OSs:
["-shared"]
=item * L</"thread_ldflags">
Windows:
[]
Other OSs:
["-pthread"]
=item * L</"static_lib_ldflag">
["-Wl,-Bstatic", "-Wl,-Bdynamic"]
=item * L</"ld_optimize">
"-O2"
=item * L</"lib_dirs">
[]
=item * L</"libs">
[]
=item * L</"after_create_link_info_cbs">
[]
=item * L</"before_link_cbs">
[]
=item * L</"output_type">
"dynamic_lib"
=item * L</"category">
"native"
=item * Other Fields
undef
=back
Exceptions:
The "file" field must be defined. Otherwise, an exception is thrown.
Exampels:
my $config = SPVM::Builder::Config->new(file => __FILE__);
=head2 new_c
my $config = SPVM::Builder::Config->new_c(file => __FILE__);
Calls L</"new"> method and sets L</"ext"> field to C<c>, and returns the return value of L</"new"> method.
=head2 new_gnu99
my $config = SPVM::Builder::Config->new_gnu99(file => __FILE__);
Calls L</"new_c"> method and sets L</"std"> field to C<gnu99>, and returns the return value of L</"new_c"> method.
=head2 new_gnu11
my $config = SPVM::Builder::Config->new_gnu11(file => __FILE__);
Calls L</"new_c"> method and sets L</"std"> field to C<gnu11>, and returns the return value of L</"new_c"> method.
=head2 new_c99
my $config = SPVM::Builder::Config->new_c99(file => __FILE__);
Calls L</"new_c"> method and sets L</"std"> field to C<c99>, and returns the return value of L</"new_c"> method.
=head2 new_c11
my $config = SPVM::Builder::Config->new_c11(file => __FILE__);
Calls L</"new_c"> method and sets L</"std"> field to C<c11>, and returns the return value of L</"new_c"> method.
=head2 new_cpp
my $config = SPVM::Builder::Config->new_cpp(file => __FILE__);
Calls L</"new"> method and sets L</"ext"> field to C<cpp> and sets L</"cc"> field to a C<C++> compiler and sets L</"ld"> field to a C<C++> linker, and returns the return value of L</"new"> method.
If C<$Config{gccversion}> contains C<clang>, L</"cc"> field and L</"ld"> field are set to C<clang++>. Otherwise, L</"cc"> field and L</"ld"> field are set to C<g++>.
=head2 new_cpp11
my $config = SPVM::Builder::Config->new_cpp11(file => __FILE__);
Calls L</"new_cpp"> method and sets L</"std"> field to C<c++11>, and returns the return value of L</"new_cpp"> method.
=head2 new_cpp14
my $config = SPVM::Builder::Config->new_cpp14(file => __FILE__);
Calls L</"new_cpp"> method and sets L</"std"> field to C<c++14>, and returns the return value of L</"new_cpp"> method.
=head2 new_cpp17
my $config = SPVM::Builder::Config->new_cpp17(file => __FILE__);
Calls L</"new_cpp"> method and sets L</"std"> field to C<c++17>, and returns the return value of L</"new_cpp"> method.
=head1 Instance Methods
=head2 add_ccflag
$config->add_ccflag(@ccflags);
Adds @ccflags to the end of L</"ccflags"> field.
=head2 add_ldflag
$config->add_ldflag(@ldflags);
Adds @ldflags to the end of L</"ldflags"> field.
=head2 add_include_dir
$config->add_include_dir(@include_dirs);
Adds @include_dirs to the end of L</"include_dirs"> field.
=head2 add_source_file
$config->add_source_file(@source_files);
Adds @source_files to the end of L</"source_files"> field.
Examples:
$config->add_source_file('foo.c', 'bar.c');
=head2 add_after_create_compile_info_cb
$config->add_after_create_compile_info_cb(@after_create_compile_info_cbs);
Adds @after_create_compile_info_cbs to the end of L</"after_create_compile_info_cbs"> field.
Examples:
$config->add_after_create_compile_info_cb(sub {
my ($config, $compile_info) = @_;
my $cc = $config->cc;
# Do something
});
=head2 add_before_compile_cb
$config->add_before_compile_cb(@before_compile_cbs);
Adds @before_compile_cbs to the end of L</"before_compile_cbs"> field.
Examples:
$config->add_before_compile_cb(sub {
my ($config, $compile_info) = @_;
my $cc_command = $compile_info->to_command;
# Do something
});
=head2 add_lib_dir
$config->add_lib_dir(@lib_dirs);
Adds @lib_dirs to the end of L</"lib_dirs"> field.
=head2 add_lib
$config->add_lib(@libs);
Adds @libs to the end of L</"libs"> field.
Examples:
$config->add_lib('gsl');
$config->add_lib('gsl', 'z');
$config->add_lib(
SPVM::Builder::LibInfo->new(config => $config, name => 'gsl'),
SPVM::Builder::LibInfo->new(config => $config, name => 'z', is_abs => 1),
);
=head2 add_lib_abs
$config->add_lib_abs(@libs);
Adds @libs to the end of L</"libs"> field with L<SPVM::Builder::LibInfo#is_abs|SPVM::Builder::LibInfo/"is_abs"> field set to a true value.
If a value in @libs is not an L<SPVM::Builder::LibInfo> object, an L<SPVM::Builder::LibInfo> object is created from the library name.
If the library is located in your user directory, it is good to use L</"add_lib_abs"> method instead of L</"add_lib"> method.
This is because if the generated dynamic link library has a relative path, that path cannot be resolved when it is loaded.
For system libraries, there is no problem because the linker knows the search directory for the library.
=head2 add_static_lib
$config->add_static_lib(@libs);
Adds @libs to the end of L</"libs"> field with L<SPVM::Builder::LibInfo#is_static|SPVM::Builder::LibInfo/"is_static"> field set to a true value.
If a value in @libs is not an L<SPVM::Builder::LibInfo> object, an L<SPVM::Builder::LibInfo> object is created from the library name.
Examples:
$config->add_static_lib('gsl');
$config->add_static_lib('gsl', 'z');
=head2 add_static_lib_abs
$config->add_static_lib_abs(@libs);
Adds @libs to the end of L</"libs"> field with L<SPVM::Builder::LibInfo#is_static|SPVM::Builder::LibInfo/"is_static"> field and L<SPVM::Builder::LibInfo#is_abs|SPVM::Builder::LibInfo/"is_abs"> field set to a true value.
If a value in @libs is not an L<SPVM::Builder::LibInfo> object, an L<SPVM::Builder::LibInfo> object is created from the library name.
=head2 add_before_link_cb
$config->add_before_link_cb(@before_link_cbs);
Adds @before_link_cbs to the end of L</"before_link_cbs"> field.
Examples:
$config->add_before_link_cb(sub {
my ($config, $link_info) = @_;
my $object_files = $link_info->object_files;
# Do something
});
=head2 use_resource
my $resource = $config->use_resource($resource_name);
my $resource = $config->use_resource($resource_name, %options);
Loads a L<resource|SPVM::Document::Resource> given a resource name and options, and returns it. The return value is an L<SPVM::Builder::Resource> object.
Options:
=over 2
=item * mode
A L<config mode|/"Config Mode"> for the resource.
=item * argv
An array reference contains L<config arguments|/"Config Arguments"> for the resource.
=back
Examples:
$config->use_resource('Resource::MyResource');
$config->use_resource('Resource::MyResource', mode => 'mode1', argv => ["option1_name" => "option1_value"]);
=head2 get_resource
my $resource = $config->get_resource($resource_name);
Gets a resource loaded by L</"use_resource"> method given a resource name, and returns it. The return value is an L<SPVM::Builder::Resource> object.
=head2 get_resource_names
my $resource_names = $config->get_resource_names;
Returns resource names loaded by L</"use_resource"> method.
=head2 load_config
my $config = $config->load_config($config_file, $argv);
Loads a config file given a config file path and an array refernce containing L<config arguments|/"Config Arguments">, and returns an L<SPVM::Builder::Config> object.
The values referenced by $argv is set to the @ARGV of the config file.
Examples:
my $config = $config->load_config(__FILE__, \@ARGV);
my $config = $config->load_config(__FILE__, []);
=head2 load_base_config
my $config = $config->load_base_config($config_file, $argv);
Creates the base config file path from the config file path $config_file, and calls L</"load_config"> method given the base config file path and config arguments, and returns its return value.
A base config file is the config file that removes its mode.
# Config file
MyClass.mode.config
# Base config file
MyClass.config
Examples:
my $config = SPVM::Builder::Config::Exe->load_base_config(__FILE__);
=head2 load_mode_config
my $config = $config->load_mode_config($config_file, $mode, $argv);
Creates a L<mode config file|/"Config Mode"> path from the config file path $config_file, and calls L</"load_config"> method given the mode config file path and config arguments, and returns its return value.
my $config = SPVM::Builder::Config::Exe->load_mode_config(__FILE__, "production");
L</"mode>" field is set to $mode.
=head2 get_loaded_config_files
Returns the config files loaded by L</"load_config"> method.
=head2 clone
my $clone = $self->clone;
Clones L<SPVM::Builder::Config> object, and returns it.
=head1 Config Mode
A config can have its mode if the config is one for an executable file generated by L<spvmcc> command and for a L<resource|SPVM::Document::Resource>.
The mode is written in the format C<.MODE_NAME> just before C<.config> extension of a config file.
C<MODE_NAME> must consist of C<a-zA-Z0-9_>.
Examples:
# production mode
MyClass.production.config
# devel mode
MyClass.devel.config
Use L</"mode"> field to get the config mode.
my $modle = $config->mode;
L<SPVM::Builder::Config#use_resource|SPVM::Builder::Config/"use_resource"> method has C<mode> option for giving a config mode.
$config->use_resource('Resource::MyResource', mode => 'production');
The L<spvmcc> command has C<--mode> option for giving a config mode.
spvmcc -o myapp --mode production myapp.spvm
=head1 Config Arguments
A config file can receive its argments.
Key-value pairs are recommended as the values of C<@ARGV> because they are normally assigned to a Perl hash.
my %options = @ARGV;
my $config = SPVM::Builder::Config->new_gnu99(file => __FILE__);
L<SPVM::Builder::Config#use_resource|SPVM::Builder::Config/"use_resource"> method has C<argv> option for giving config arguments.
$config->use_resource('Resource::MyResource', argv => [option_name => "option_value"]);
The L<spvmcc> command has C<--config-argv> option(released in the near future) for giving config arguments.
spvmcc -o myapp --config-argv option_name --config-argv option_value myapp.spvm
The L<spvmcc> command also has C<--config-argv-option> option(released in the near future) to write config arguments easily.
spvmcc -o myapp --config-argv-option option_name=option_value myapp.spvm
=head1 Library Path Resolution
The following is the rule of library path resolution.
Library names are converted to L<SPVM::Builder::LibInfo> objects.
If L<SPVM::Builder::LibInfo#is_abs|SPVM::Builder::LibInfo/"is_abs"> field is a false value, the linker L</"ld"> resolves libaray paths.
If L<SPVM::Builder::LibInfo#is_abs|SPVM::Builder::LibInfo/"is_abs"> field is a true value, libaray paths are resolved by the following rules.
A library is searched in the library search directories contained in L</"lib_dir"> field from the beginning.
If L<SPVM::Builder::LibInfo#is_static|SPVM::Builder::LibInfo/"is_static"> field is a false value, the search is performed in the order of a dynamic library, a static library.
If L<SPVM::Builder::LibInfo#is_static|SPVM::Builder::LibInfo/"is_static"> field is a true value, the search is performed only in static libraries.
If a library is found, C<-l> option of the linker L</"ld"> is created using the found absolute path.
=head1 Examples
GNU C99:
my $config = SPVM::Builder::Config->new_gnu99(file => __FILE__);
C99:
my $config = SPVM::Builder::Config->new_c99(file => __FILE__);
C11:
my $config = SPVM::Builder::Config->new_c11(file => __FILE__);
C++:
my $config = SPVM::Builder::Config->new_cpp(file => __FILE__);
C++11:
my $config = SPVM::Builder::Config->new_cpp11(file => __FILE__);
Output messages to C<stderr> from the compiler and the linker:
$config->quiet(0);
Force the compilation and link:
$config->force(1);
=head1 Copyright & License
Copyright (c) 2023 Yuki Kimoto
MIT License