App-hopen
view release on metacpan or search on metacpan
lib/App/hopen.pm view on Meta::CPAN
# App::hopen: Implementation of the hopen(1) program
package App::hopen;
our $VERSION = '0.000015';
# Imports {{{1
use strict; use warnings;
use Data::Hopen::Base;
use App::hopen::AppUtil ':all';
use App::hopen::BuildSystemGlobals;
use App::hopen::Phases qw(:default phase_idx next_phase);
use App::hopen::Util qw(isMYH MYH);
use App::hopen::Util::String qw(line_mark_string);
use Data::Hopen qw(:default loadfrom *VERBOSE *QUIET);
use Data::Hopen::Scope::Hash;
use Data::Hopen::Scope::Environment;
use Data::Hopen::Util::Data qw(dedent forward_opts);
use Data::Dumper;
use File::Path::Tiny;
use File::stat ();
use Getopt::Long qw(GetOptionsFromArray :config gnu_getopt);
use Hash::Merge;
use Path::Class;
use Scalar::Util qw(looks_like_number);
BEGIN { $Data::Dumper::Indent = 1; } # DEBUG
# }}}1
# Documentation {{{1
=pod
=encoding UTF-8
=head1 NAME
App::hopen - Graph-driven cross-platform build system
=head1 CURRENT STATUS
Most features are not yet implemented ;) . However it will generate a
C<Makefile> or C<build.ninja> file for a C C<Hello, World> program at this
point! It can generate command lines for gcc(1) or for Microsoft's C<cl.exe>.
=head1 INTRODUCTION
hopen is a cross-platform software build generator. It makes files you can
pass to Make, Ninja, Visual Studio, or other build tools, to compile and
link your software. hopen gives you:
=over
=item *
A full, Turing-complete, robust programming language to write your
build scripts (specifically, Perl 5.14+)
=item *
No hidden magic! All your data is visible and accessible in a build graph
(whence "graph-driven").
=item *
Context-sensitivity. Your users can tweak their own builds for their own
platforms without affecting your project.
=back
See L<App::hopen::Conventions> for details of the input format.
Why Perl? Because (1) you probably already have it installed, and
(2) it is the original write-once, run-everywhere language!
=head2 Example
Create a file C<.hopen.pl> in your source tree. Then:
$ hopen
From ``.'' into ``built''
Running Check phase
Now C<built/MY.hopen.pl> has been created, and loaded with information about
your configuration. You can edit that file if you want to change what will
lib/App/hopen.pm view on Meta::CPAN
grep { (scalar @{$CMDLINE_OPTS{ $_ }})==3 }
keys %CMDLINE_OPTS
);
# Get options
my $opts_ok = GetOptionsFromArray(
$params{from}, # source array
$hrOptsOut, # destination hash
'usage|?', 'h|help', 'man', # options we handle here
map { $_->[0] . ($_->[1] // '') } values %CMDLINE_OPTS, # options strs
);
# Help, if requested
if(!$opts_ok || have('usage') || have('h') || have('man')) {
# Only pull in the Pod routines if we actually need them.
# Terminal formatting, if present.
{
no warnings 'once';
eval "require Pod::Text::Termcap";
$Pod::Usage::Formatter = 'Pod::Text::Termcap' unless $@;
}
require Pod::Usage;
my @in = (-input => __FILE__);
Pod::Usage::pod2usage(-verbose => 0, -exitval => EXIT_PARAM_ERR, @in)
unless $opts_ok; # unknown opt
Pod::Usage::pod2usage(-verbose => 0, -exitval => EXIT_OK, @in)
if have('usage');
Pod::Usage::pod2usage(-verbose => 1, -exitval => EXIT_OK, @in)
if have('h');
# --man: suppress "INTERNAL" sections. Note that this does
# get rid of the automatic pager we would otherwise get
# by virtue of pod2usage's invoking perldoc(1). Oh well.
Pod::Usage::pod2usage(
-exitval => EXIT_OK, @in, # VVV see Pod::Select for format
-verbose => 99, -sections => '!INTERNAL.*'
) if have('man');
}
# Map the option names from GetOptions back to the internal names we use,
# e.g., $hrOptsOut->{EVAL} from $hrOptsOut->{e}.
my %revmap = map { $CMDLINE_OPTS{$_}->[0] => $_ } keys %CMDLINE_OPTS;
for my $optname (keys %$hrOptsOut) {
$hrOptsOut->{ $revmap{$optname} } = $hrOptsOut->{ $optname };
}
# Process other arguments. The first two non-option arguments are dest
# dir and project dir, if --from and --to were not given.
$hrOptsOut->{DEST_DIR} //= $params{from}->[0] if @{$params{from}};
$hrOptsOut->{PROJ_DIR} //= $params{from}->[1] if @{$params{from}}>1;
# Sanity check VERBOSE2, and give it a default of 0
my $v2 = $hrOptsOut->{VERBOSE2} // 0;
$v2 = 1 if $v2 eq ''; # --verbose without value === --verbose=1
die "--verbose requires a positive numeric argument"
if (defined $v2) && ( !looks_like_number($v2) || (int($v2) < 0) );
$hrOptsOut->{VERBOSE2} = int($v2 // 0);
} #_parse_command_line() }}}2
# }}}1
# === Main worker code ================================================== {{{1
sub _execute_hopen_file { # Load and run a single hopen file {{{2
=head2 _execute_hopen_file
Execute a single hopen file, but B<do not> run the DAG. Usage:
_execute_hopen_file($filename[, options...])
This function takes input from L</$_hrData> unless a C<< DATA=>{...} >> option
is given. This function updates L</$_hrData> based on the results.
Options are:
=over
=item phase
If given, force the phase to be the one specified.
=item quiet
If truthy, suppress extra output.
=item libs
If given, it must be an arrayref of directories. Each of those will be
turned into a C<use lib> statement (see L<lib>) in the generated source.
=back
=cut
my $fn = shift or croak 'Need a file to run';
my %opts = @_;
$Phase = $opts{phase} if $opts{phase};
my $merger = Hash::Merge->new('RETAINMENT_PRECEDENT');
# == Set up code pieces related to phase control ==
my ($set_phase, $cannot_set_phase, $cannot_set_phase_warn);
my $setting_phase_allowed = false;
# Note: all phase-setting functions succeed if there was nothing
# for them to do!
$set_phase = q(
sub can_set_phase { true }
sub set_phase {
my $new_phase = shift or croak 'Need a phase';
return if $App::hopen::BuildSystemGlobals::Phase eq $new_phase;
croak "Phase $new_phase is not one of the ones I know about (" .
( run in 1.786 second using v1.01-cache-2.11-cpan-364913b4093 )