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 )