#!perl

use strict;
use warnings;

use SPVM();
use SPVM::Builder::Exe;
use SPVM::Builder::Util;
use FindBin;

SPVM::Builder::Util::getopt
  # spvmcc and spvm shared
  'h|help'          => \my $help,
  'v|version'       => \my $show_version,
  'I=s' => \my @inc,
  'B|build-dir=s'   => \my $build_dir,
  'w'               => \my $warning,
  
  # spvmcc only
  'o|output=s'        => \my $output_file,
  'q|quiet'   => \my $quiet,
  'f|force' => \my $force,
  'no-config' => \my $allow_no_config_file,
  'm|mode=s' => \my $mode,
  'ccflag=s' => \my @ccflags_global,
  'ccflag-spvm=s' => \my @ccflags_spvm,
  'ccflag-native=s' => \my @ccflags_native,
  'ccflag-native-class=s' => \my @ccflags_native_class_strings,
  'ccflag-precompile=s' => \my @ccflags_precompile,
  'define=s' => \my @defines_global,
  'define-spvm=s' => \my @defines_spvm,
  'define-native=s' => \my @defines_native,
  'define-native-class=s' => \my @defines_native_class_strings,
  'define-precompile=s' => \my @defines_precompile,
  'optimize=s' => \my $optimize_global,
  'optimize-spvm=s' => \my $optimize_spvm,
  'optimize-native=s' => \my $optimize_native,
  'optimize-native-class=s' => \my @optimize_native_class_strings,
  'optimize-precompile=s' => \my $optimize_precompile,
  'include-dir=s' => \my @include_dirs_global,
  'include-dir-spvm=s' => \my @include_dirs_spvm,
  'include-dir-native=s' => \my @include_dirs_native,
  'include-dir-native-class=s' => \my @include_dirs_native_class_strings,
  'include-dir-precompile=s' => \my @include_dirs_precompile,
  '--object-file=s' => \my @external_object_files,
  '--build-spvm-archive' => \my $build_spvm_archive,
  '--load-spvm-archive=s' => \my $spvm_archive, # This is not working well yet, and undocumented.
;

if ($help) {
  print SPVM::Builder::Util::extract_usage;
  exit 0;
}
elsif ($show_version) {
  my $version_string = "spvmcc v$SPVM::VERSION";
  print "$version_string\n";
  exit 0;
}

my $script_name = shift;

my $source = "";

$0 = $script_name;

FindBin::again();

eval { $source = SPVM::Builder::Util::slurp_binary($script_name); };

my $inc_by_lib_directive = SPVM::Builder::Util::parse_lib_directive($source, $FindBin::Bin);

unshift @INC, map { $_ =~ s/[\\\/]SPVM$//; $_; } @inc, @$inc_by_lib_directive;

if ($warning) {
  $^W = 1;
}

unless (defined $script_name) {
  warn "<script_name> is required.\n\n";
  warn SPVM::Builder::Util::extract_usage;
  exit 255;
}

unless (defined $output_file) {
  warn "-o, --output option is required.\n\n";
  warn SPVM::Builder::Util::extract_usage;
  exit 255;
}

my $ccflags_native_class = SPVM::Builder::Exe->parse_option_values_native_class('ccflag-native-class', \@ccflags_native_class_strings, 1);

my $defines_native_class = SPVM::Builder::Exe->parse_option_values_native_class('define-native-class', \@defines_native_class_strings, 1);

my $optimize_native_class = SPVM::Builder::Exe->parse_option_values_native_class('optimize-native-class', \@optimize_native_class_strings, 0);

my $include_dirs_native_class = SPVM::Builder::Exe->parse_option_values_native_class('include-dir-native-class', \@include_dirs_native_class_strings, 1);

my $build_exe = SPVM::Builder::Exe->new(
  script_name => $script_name,
  build_dir => $build_dir,
  output_file => $output_file,
  quiet => $quiet,
  force => $force,
  allow_no_config_file => $allow_no_config_file,
  mode => $mode,
  ccflags_global => \@ccflags_global,
  ccflags_spvm => \@ccflags_spvm,
  ccflags_native => \@ccflags_native,
  ccflags_native_class => $ccflags_native_class,
  ccflags_precompile => \@ccflags_precompile,
  defines_global => \@defines_global,
  defines_spvm => \@defines_spvm,
  defines_native => \@defines_native,
  defines_native_class => $defines_native_class,
  defines_precompile => \@defines_precompile,
  optimize_global => $optimize_global,
  optimize_spvm => $optimize_spvm,
  optimize_native => $optimize_native,
  optimize_native_class => $optimize_native_class,
  optimize_precompile => $optimize_precompile,
  include_dirs_global => \@include_dirs_global,
  include_dirs_spvm => \@include_dirs_spvm,
  include_dirs_native => \@include_dirs_native,
  include_dirs_native_class => $include_dirs_native_class,
  include_dirs_precompile => \@include_dirs_precompile,
  external_object_files => \@external_object_files,
  spvm_archive => $spvm_archive,
  build_spvm_archive => $build_spvm_archive,
);

$build_exe->build_exe_file;

=encoding utf8

=head1 Name

spvmcc - Generating Excutable File

=head1 Description

The spvmcc command generates an executable file from SPVM classes.

=head1 Usage

  Usage: spvmcc [OPTIONS] SCRIPT_NAME
    
    spvmcc -o myapp myapp.spvm
    
    spvmcc -I lib/SPVM -o myapp myapp.spvm
  
  Options:
    -h, --help                      Shows this message
    -v, --version                   Shows the version
    -o, --output FILE               The output file name
    -I DIR                          Adds a include directory
    -B, --build-dir DIR             Build diretory
    -q, --quiet                     Stops the output of messages
    -f, --force                     Forces the compile and link
    --no-config                     No configration file is ok
    -m, --mode MODE                 Config mode
    -w                              Enable warnings
    --ccflag VALUE                  Compiler flag
    --ccflag-spvm VALUE             Same as --ccflag,
                                    but only SPVM core source files
                                    and a bootstrap file
    --ccflag-native VALUE           Same as --ccflag,
                                    but only native class source files
                                    and native source files
                                    
    --ccflag-native-class CLASS_NAME@VALUE       Same as --ccflag-native,
                                                 but only a class
                                             
    --ccflag-precompile VALUE       Same as --ccflag,
                                    but only precompile class source files
    --define VALUE                  Same as --ccflag, but -D is prefixed.
    --define-spvm VALUE             Same as --define,
                                    but only SPVM core source files
                                    and a bootstrap file
    --define-native VALUE           Same as --define,
                                    but only native class source files
                                    and native source files
                                    
    --define-native-class CLASS_NAME@VALUE       Same as --define-native,
                                                 but only a class
                                             
    --define-precompile VALUE       Same as --define,
                                    but only precompile class source files
    --optimize LEVEL                Optimization level such as "-O3", "-O0 -g".
    --optimize-spvm LEVEL           Same as --optimize,
                                    but only SPVM core source files
                                    and a bootstrap file
    --optimize-native LEVEL         Same as --optimize,
                                    but only native class source files
                                    and native source files
                                    
    --optimize-native-class CLASS_NAME@LEVEL     Same as --optimize-native,
                                                 but only a class
                                               
    --optimize-precompile LEVEL     Same as --optimize,
                                    but only precompile class source files
    --include-dir DIR               Same as --ccflag, but -I is prefixed.
    --include-dir-spvm DIR          Same as --include-dir,
                                    but only SPVM core source files
                                    and a bootstrap file
    --include-dir-native DIR        Same as --include-dir,
                                    but only native class source files
                                    and native source files
                                    
    --include-dir-native-class CLASS_NAME@DIR    Same as --include-dir-native,
                                                 but only a class
                                             
    --include-dir-precompile DIR    Same as --include-dir,
                                    but only precompile class source files
    --build-spvm-archive            Generate an SPVM archive

=head1 Details

=head2 spvmcc Command

  spvmcc [OPTIONS] SCRIPT_NAME

The C<spvmcc> command generates an executable file from SPVM classes.

I<OPTIONS> are L<options|/"Options">.

I<SCRIPT_NAME> is a script path that contains an SPVM script.
  
  # myapp.spvm
  use Fn;
  my $var = 1;
  say $var;

Anon class that contains a L<bootstrap method|SPVM::Document::Language::Class/"Bootstrap Method"> is allowed as an SPVM script.

  # myapp.spvm
  class {
    
    version "1.001";
    
    our $FOO : int;
    
    use Fn;
    
    static method main : void () {
      my $var = 1;
      say $var;
    }
  }

The base name of I<SCRIPT_NAME> must be a L<Script Base Name|spvm/"Script Base Name">. Otherwise an exception is thrown.

See L<Class Search Directories|SPVM::Document::Language::Class/"Class Search Directories"> about default class search directories.

See L<SPVM::Document::EnvironmentVariables> about available environment variables.

=head2 Config File for Executable File

A config file that corresponding to the script name must exist for an executable file except for the case that L<"--no-config"> is specified.

The config for an executable file is a L<SPVM::Builder::Config::Exe> object.

C<myapp.config>:

  use SPVM::Builder::Config::Exe;
  
  my $exe_config = SPVM::Builder::Config::Exe->new;
  
  $exe_config

=head2 Caching System

Compiled object files and an executable file are not rebuilt unless they need to be rebuilt.

L</"--force"> option forces the rebuild.

=head2 Resources

There are important points to be aware of when generating executable files. That is, L<resources|SPVM::Document::Resource> are not automatically compiled.

When you run an SPVM program with the L<spvm> command, the resources are contained within the shared library of each class. Therefore, there are no conflicts between resources.

However, in the case of executable files, there are resource conflicts. For this, the resources must be resolved manually in the configuration file.

  $config->use_resource('Resource::Zlib');

This is hard work, but given that the executable file must be compiled from source files and run on a variety of platforms, I think that solving it manually is a better way.

I have published a command that allows you to view the list of classes using resources and the resource settings.

See also L<--resource-info|spvmdeps/"--resource-info"> option in L<spvmdeps> command to dump resource information.

=head2 lib Directive

If the source code specified by I<SCRIPT_NAME> contains lib directives, The directories specified by lib directive is prepeneded to L<class search directories|SPVM::Document::Language::Class/"Class Search Directories">.
  
  #lib "$FindBin::Bin/lib"

This directories specified by lib directive is placed after the directories specified by L</"--include-dir"> option.

=head2 SPVM Archive

An SPVM archive is a tarball (C<.tar.gz>) containing SPVM class files and object files generated from native classes, precompilation classes, and resource classes.

C<spvmcc.json> in an SPVM archive has archive information. This contains the application name, the mode, the version and class information.

The recommended file extension is C<.spvm-archive.tar.gz>.

An SPVM archive does not contains external object files(C<.o>), static library files(C<.a>), dynamic library files(C<.so>, C<.dll>, etc), and SPVM anon class files.

An SPVM archive can be created by C<--build-spvm-archive> option.

  spvmcc --build-spvm-archive -o $exe_dir/myapp.spvm-archive.tar.gz myapp.spvm

An SPVM archive can be load by L<SPVM::Builder::Config::Exe/"load_spvm_archive"> method.

By loading an SPVM archive, you can load its classes, and its object files are linked.

If an SPVM archive is loaded and C<--build-spvm-archive> option is specified, the current application information is merged with the SPVM archive information, and then the merged SPVM archive is output.

=head1 Options

=head2 --help

Outputs how to use the C<spvmcc> command to standard output.

=head2 -h

  -h

Same as L</"--help">.

=head2 --version

Outputs the version of the C<spvmcc> command to standard output. This version is the same as the version of L<SPVM>.

=head2 -v

  -v

Same as L</"--version">.

=head2 -I

  -I DIR

Prepends I<DIR> to L<class search directories|SPVM::Document::Language::Class/"Class Search Directories">

This option can be specified multiple times.

  -I dir1 -I dir2

In this case, class search directories becomes the following.

  [dir1, dir2, default_dirs]

=head2 --build-dir

  --build-dir DIR

Sets L<SPVM_BUILD_DIR|SPVM::Document::EnvironmentVariables/"SPVM_BUILD_DIR"> environment variable to I<DIR>.

=head2 -B

  -B DIR

Same as L</"--build-dir">.

=head2 --output

  --output FILE

Specifies the output file path I<FILE>. This output file is an executable file.

=head2 -o

  -o FILE

Same as L</"--output">.

=head2 --quiet

  --quiet

Suppresses messages from the C<spvmcc> command.

=head2 -q

  -q

Same as L</"--quiet">.

=head2 --force

  --force

Forces the rebuild of object files and an executable file.

=head2 -f

  -f

Same as L</"--force">.

=head2 --no-config

If this option is specified and a config file does not exist, the C<spvmcc> command runs without finising the program.

=head2 --mode

  --mode MODE

Specifies the config mode I<MODE>.

See L<Config Mode|SPVM::Builder::Config/"Config Mode"> about config modes.

=head2 -m

  -m MODE

Same as L</"--mode">.

=head2 -w

  -w

Enables warning flag.

Implementation:

Sets L<CommandInfo#WARNING|SPVM::CommandInfo> class variable to 1.

=head2 --ccflag

  --ccflag VALUE

A compiler flag.

This affects all source files.

This option can be repeated.

=head2 --ccflag-spvm

  --ccflag-spvm VALUE

A compiler flag.

This affects SPVM core source files and a bootstrap file.

This option can be repeated.

=head2 --ccflag-native

  --ccflag-native VALUE

A compiler flag.

This affects native class source files(such as C<MyClass.c>) and native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

=head2 --ccflag-native-class

  --ccflag-native-class CLASS_NAME@VALUE

A compiler flag.

This affects a native class source file specified by I<CLASS_NAME>(such as C<MyClass.c>) and its native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

Examples:

  --ccflag-native-class MyClass@-Wall

=head2 --ccflag-precompile

  --ccflag-precompile VALUE

A compiler flag.

This affects precompile class source files.

This option can be repeated.

=head2 --define

  --define VALUE

A compiler flag, but C<-D> is prefiex.

This affects all source files.

This option can be repeated.

=head2 --define-spvm

  --define-spvm VALUE

A compiler flag, but C<-D> is prefiex.

This affects SPVM core source files and a bootstrap file.

This option can be repeated.

=head2 --define-native

  --define-native VALUE

A compiler flag, but C<-D> is prefiex.

This affects native class source files(such as C<MyClass.c>) and native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

=head2 --define-native-class

  --define-native-class VALUE

A compiler flag, but C<-D> is prefiex.

This affects a native class source file specified by I<CLASS_NAME>(such as C<MyClass.c>) and its native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

Examples:
  
  # Expanded to -DDEBUG_MEMORY
  --define-native-class MyClass@DEBUG_MEMORY

=head2 --define-precompile

  --define-precompile VALUE

A compiler flag, but C<-D> is prefiex.

This affects precompile class source files.

This option can be repeated.

=head2 --optimize

  --optimize LEVEL

Optimization level.

This affects all source files.

Examples:

  --optimize "-O0 -g"

=head2 --optimize-spvm

  --optimize-spvm LEVEL

Optimization level.

This affects SPVM core source files and a bootstrap file.

=head2 --optimize-native

  --optimize-native LEVEL

Optimization level.

This affects native class source files(such as C<MyClass.c>) and native source files(such as C<MyClass.native/src/foo>).

=head2 --optimize-native-class

  --optimize-native-class LEVEL

Optimization level.

This affects a native class source file specified by I<CLASS_NAME>(such as C<MyClass.c>) and its native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

Examples:

  --optimize-native-class "MyClass@-O0 -g"

=head2 --optimize-precompile

  --optimize-precompile LEVEL

Optimization level.

This affects precompile class source files.

=head2 --include-dir

  --include-dir DIR

A compiler flag, but C<-I> is prefiex.

This affects all source files.

This option can be repeated.

=head2 --include-dir-spvm

  --include-dir-spvm DIR

A compiler flag, but C<-I> is prefiex.

This affects SPVM core source files and a bootstrap file.

This option can be repeated.

=head2 --include-dir-native

  --include-dir-native DIR

A compiler flag, but C<-I> is prefiex.

This affects native class source files(such as C<MyClass.c>) and native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

=head2 --include-dir-native-class

  --include-dir-native-class DIR

A compiler flag, but C<-I> is prefiex.

This affects a native class source file specified by I<CLASS_NAME>(such as C<MyClass.c>) and its native source files(such as C<MyClass.native/src/foo>).

This option can be repeated.

Examples:

  --include-dir-native-class MyClass@/path/foo

=head2 --include-dir-precompile

  --include-dir-precompile DIR

A compiler flag, but C<-I> is prefiex.

This affects precompile class source files.

This option can be repeated.

=head2 --object-file

  --object-file OBJECT_FILE

An additional object file I<OBJECT_FILE> passed to the linker.

This option can be repeated.

=head2 --build-spvm-archive

  --build-spvm-archive

Generate an SPVM archive into the path of C<-o> option. The recomennded file extension of an SPVM archive is a tarball(.spvm-archive.tar.gz).

=head1 Copyright & License

Copyright 2023 Yuki Kimoto. All Rights Reserved.

MIT License.
