Skip to content

Commit

Permalink
Added App::hopen::Util::BasedPath
Browse files Browse the repository at this point in the history
- Added module BasedPath
- Added some tests for BasedPath
- Modified H::files to use BasedPath
- Modified C toolchain to generate BasedPath instances
- Modified Makefile generator to use BasedPath instances [WIP]

Also:
- Permit hopen files to run set_phase if all it does is try to set the
  phase to what it already is.
- Removed stale references to Data::Hopen in App::hopen::Conventions.
- Fix the link to App::hopen::Conventions in README.md
  • Loading branch information
Chris White committed Feb 25, 2019
1 parent 0c74f7c commit f6c4a85
Show file tree
Hide file tree
Showing 13 changed files with 328 additions and 45 deletions.
2 changes: 2 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ lib/App/hopen/T/Gnu.pm
lib/App/hopen/T/Gnu/C.pm
lib/App/hopen/Tool.pm
lib/App/hopen/Toolchain.pm
lib/App/hopen/Util/BasedPath.pm
LICENSE.md
Makefile.PL
MANIFEST This list of files
Expand All @@ -39,6 +40,7 @@ t/011-scope-env.t
t/012-scope-nested.t
t/020-dag.t
t/021-dag-single-goal.t
t/030-util-basedpath.t
t/200-apputil-basic.t
t/dir200/inner.hopen.pl
t/dir200/inner/.hopen.pl
Expand Down
2 changes: 1 addition & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ OPTIONS
Specify the destination directory. Overrides a destination directory
given as a positional argument.

--phase
--phase "phase"
Specify which phase of the process to run. Note that this overrides
whatever is specified in any MY.hopen.pl file, so may cause unexpected
results!
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ build scripts (specifically, Perl 5.14+)
- Context-sensitivity. Your users can tweak their own builds for their own
platforms without affecting your project.

See [App::hopen::Conventions](https://metacpan.org/pod/App::hopen::Conventions) for details of the input format.
See [App::hopen::Conventions](https://github.com/hopenbuild/App-hopen/blob/master/lib/App/hopen/Conventions.pod) 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!
Expand Down Expand Up @@ -63,7 +63,7 @@ If no destination directory is specified, `<project dir>/built` is used.
Specify the destination directory. Overrides a destination directory given
as a positional argument.

- --phase
- --phase `phase`

Specify which phase of the process to run. Note that this overrides whatever
is specified in any MY.hopen.pl file, so may cause unexpected results!
Expand Down
14 changes: 13 additions & 1 deletion lib/App/hopen.pm
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,14 @@ turned into a C<use lib> statement (see L<lib>) in the generated source.
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 (" .
join(', ', @PHASES) . ')'
unless defined phase_idx($new_phase);
Expand All @@ -307,13 +311,17 @@ turned into a C<use lib> statement (see L<lib>) in the generated source.
$cannot_set_phase = q(
sub can_set_phase { false }
sub set_phase {
my $new_phase = shift // '';
return if $App::hopen::BuildSystemGlobals::Phase eq $new_phase;
croak "I'm sorry, but this file (``$FILENAME'') is not allowed to set the phase"
}
);

$cannot_set_phase_warn = q(
sub can_set_phase { false }
sub set_phase {
my $new_phase = shift // '';
return if $App::hopen::BuildSystemGlobals::Phase eq $new_phase;
) .
($opts{quiet} ? '' :
q(
Expand Down Expand Up @@ -361,6 +369,10 @@ turned into a C<use lib> statement (see L<lib>) in the generated source.
# If this were not the case, every second or subsequent run
# of hopen(1) would croak if --phase were specified!
$phase_text .= isMYH($fn) ? $cannot_set_phase_warn : $cannot_set_phase;
# TODO? permit regular hopen files to set the the phase if
# neither MYH nor the command line did, and we're at the first
# phase. This is so the hopen file can say `set_phase 'Gen';`
# if there's nothing to do during Check.
}
} #endif -e else

Expand Down Expand Up @@ -822,7 +834,7 @@ name or the part after C<App::hopen::T::>.
Specify the destination directory. Overrides a destination directory given
as a positional argument.
=item --phase
=item --phase C<phase>
Specify which phase of the process to run. Note that this overrides whatever
is specified in any MY.hopen.pl file, so may cause unexpected results!
Expand Down
29 changes: 18 additions & 11 deletions lib/App/hopen/Conventions.pod
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
=head1 NAME

Data::Hopen::Conventions - conventions for using hopen(1) as a build system
App::hopen::Conventions - conventions for using hopen(1) as a build system

=head1 SYNOPSIS

L<Data::Hopen> is a flexible dataflow processor and task runner. However, its
L<App::hopen> is a flexible dataflow processor and task runner. However, its
main use case is as a build system (e.g., a Makefile generator). To keep
the core of hopen as flexible as possible, hopen(1) (build-system use cases)
requires its components to follow the conventions described in this document.

These conventions are generally implemented/enforced in L<Data::Hopen::App>
and L<Data::Hopen::AppUtil>.
These conventions are generally implemented/enforced in L<App::hopen>
and L<App::hopen::AppUtil>.

Note: everything in this document is subject to change since Data::Hopen
Note: everything in this document is subject to change since C<App::hopen>
is currently under active development.

=head1 COMPONENTS USED BY THE BUILD SYSTEM
Expand All @@ -30,7 +30,7 @@ The default directory is C<< <project dir>/built >>.

=item Generator

A subclass of L<Data::Hopen::Gen> that creates the build-system files.
A subclass of L<App::hopen::Gen> that creates the build-system files.
The generator's job is to arrange for coordinating work that needs to be done.

=item Toolset
Expand Down Expand Up @@ -127,33 +127,40 @@ starting with C<__R> are reserved.
The hopen file is executed in scalar context.

Each hopen file runs in an environment with everything from the following
packages loaded (via L<Data::Hopen::HopenFileKit>):
packages loaded (via L<App::hopen::HopenFileKit>):

=over

=item * L<App::hopen::BuildSystemGlobals>

=item * L<App::hopen::Phases>

=item * L<App::hopen::Util::BasedPath>

=item * L<Data::Hopen>

=item * L<Data::Hopen::Base>

=item * L<Data::Hopen::Phases>
This also gives you L<Carp>, L<Data::Dumper>, L<strict>, and L<warnings>,
and requires Perl 5.14+.

=item * L<Path::Class>

=back

=head2 Variables usable in hopen files

These are defined in L<Data::Hopen>.
These are defined in L<App::hopen::BuildSystemGlobals>.

=over

=item $Generator

The current L<Data::Hopen::Gen> instance.
The current L<App::hopen::Gen> instance.

=item $Toolset

The name of the current C<< Data::Hopen::T::<stem> >> package root.
The name of the current C<< App::hopen::T::<stem> >> package root.

=item $Build

Expand Down
47 changes: 38 additions & 9 deletions lib/App/hopen/Gen/Make.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
package App::hopen::Gen::Make;
use Data::Hopen::Base;

BEGIN { $Data::Dumper::Indent = 1; } # DEBUG

our $VERSION = '0.000009'; # TRIAL

use parent 'App::hopen::Gen';
use Class::Tiny {
targets => sub { Hash::Ordered->new() }
};

use App::hopen::BuildSystemGlobals;
use App::hopen::Phases qw(is_last_phase);
use Data::Hopen qw(:default getparameters $QUIET);
use Hash::Ordered;
Expand Down Expand Up @@ -49,7 +52,8 @@ sub visit_goal {

#=head2 visit_node
#
#TODO
# TODO Build the list of work to do each time we visit a node, rather
# than requiring the generator to build the work[] array.
#
#=cut
#
Expand All @@ -72,7 +76,7 @@ sub finalize {
# During the Gen phase, create the Makefile
open my $fh, '>', $self->dest_dir->file('Makefile') or die "Couldn't create Makefile";
print $fh <<EOT;
# Makefile generated by hopen (https://github.com/cxw42/hopen)
# Makefile generated by hopen (https://github.com/hopenbuild/App-hopen)
# at @{[scalar gmtime]} GMT
# From ``@{[$self->proj_dir->absolute]}'' into ``@{[$self->dest_dir->absolute]}''
Expand All @@ -95,7 +99,22 @@ EOT
hlog { 'Work to do', Dumper(\@work) } 3;
foreach my $item (@work) {
next unless @{$item->{from}}; # no prerequisites => assume it's a file
say $fh $item->{to}->[0], ': ', join(' ', @{$item->{from}});
my @sources;
foreach(@{$item->{from}}) {
hlog { 'Work item', Dumper($_) } 3;
next unless $_;
push @sources, $_->path_on($ProjDir)->relative($DestDir);
# TODO FIXME this isn't right --- sources may be in the
# project dir or the destination dir.
# Move this logic to the toolchain?
}

my $dest = $item->{to}->[0];
$dest = $dest->path_on($DestDir)->relative($DestDir)
if $dest->DOES('App::hopen::Util::BasedPath');
# TODO is this right?

say $fh $dest, ': ', join(' ', @sources);
say $fh (_expand($item) =~ s/^/\t/gmr);
say $fh '';
}
Expand Down Expand Up @@ -124,14 +143,24 @@ L</finalize>.

sub _expand {
my $item = shift or croak 'Need a work item';
hlog { __PACKAGE__ . '::expand()', Dumper($item) } 2;
my $out = $item->{how} or return ''; # no `how` => no output; not an error
hlog { __PACKAGE__ . '::_expand()', Dumper($item) } 2;
my $retval = $item->{how} or return ''; # no `how` => no output; not an error
$retval = $retval->[0] if ref $retval eq 'ARRAY';

# TODO FIXME HACK --- this isn't right, but it's a start.
my $first = $item->{from}->[0];
$first = $first->path_on($DestDir)->relative($DestDir)
if $first->DOES('App::hopen::Util::BasedPath');

my $out = $item->{to}->[0];
$out = $out->path_on($DestDir)->relative($DestDir)
if $out->DOES('App::hopen::Util::BasedPath');

$out =~ s{#first\b}{$item->{from}->[0] // ''}ge; # first input
$out =~ s{#all\b}{join(' ', @{$item->{from}})}ge; # all inputs
$out =~ s{#out\b}{$item->{to}->[0] // ''}ge;
$retval =~ s{#first\b}{$first // ''}ge; # first input
$retval =~ s{#all\b}{join(' ', @{$item->{from}})}ge; # all inputs
$retval =~ s{#out\b}{$out // ''}ge;

return $out;
return $retval;
} #_expand()

1;
Expand Down
10 changes: 9 additions & 1 deletion lib/App/hopen/H.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ BEGIN {
);
}

use App::hopen::BuildSystemGlobals;
use App::hopen::G::FilesOp;
use App::hopen::Util::BasedPath;
use Data::Hopen qw(hlog getparameters);
use Data::Hopen::G::GraphBuilder;
use Data::Hopen::Util::Data qw(forward_opts);
Expand Down Expand Up @@ -46,13 +48,19 @@ Creates a DAG node representing a set of input files. Example usage:
The node is a L<Data::Hopen::G::FilesOp>.
The file path is assumed to be relative to the current project directory.
TODO handle subdirectories.
=cut

sub files {
my ($builder, %args) = getparameters('self', ['*'], @_);
hlog { __PACKAGE__, 'files:', Dumper(\%args) } 3;
my @files = @{$args{'*'} // []};
@files = map { based_path(path => file($_), base => $ProjDir) } @files;
hlog { __PACKAGE__, 'file objects:', @files } 3;
return App::hopen::G::FilesOp->new(
files => [ map { file($_)->absolute } @{$args{'*'} // []} ],
files => [ @files ],
forward_opts(\%args, 'name')
);
} #files()
Expand Down
27 changes: 17 additions & 10 deletions lib/App/hopen/HopenFileKit.pm
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# App::hopen::HopenFileKit - set up a hopen file
package App::hopen::HopenFileKit;

# What we use
use Data::Hopen qw(:default loadfrom);
use Data::Hopen::Base;
use App::hopen::BuildSystemGlobals;

use Import::Into;
use Package::Alias ();

# What we export to the caller
use Data::Hopen ();
use App::hopen::Phases ();
# Imports. Note: `()` marks packages we export to the caller but
# don't use ourselves. These are in the same order as in import().
use App::hopen::BuildSystemGlobals;
use App::hopen::Util::BasedPath ();
use Path::Class ();

use App::hopen::Phases ();
use Data::Hopen qw(:default loadfrom);


our $VERSION = '0.000009'; # TRIAL

use parent 'Exporter'; # Exporter-exported symbols {{{1
Expand Down Expand Up @@ -131,10 +133,15 @@ Set up the calling package. See L</SYNOPSIS> for usage.
__PACKAGE__->export_to_level(1, @args);

# Re-export packages
$_->import::into($target) foreach qw(Data::Hopen::Base Path::Class
App::hopen::BuildSystemGlobals);
Data::Hopen->import::into($target, ':all');
$_->import::into($target) foreach qw(
Data::Hopen::Base
App::hopen::BuildSystemGlobals
App::hopen::Util::BasedPath
Path::Class
);

App::hopen::Phases->import::into($target, qw(:all :hopenfile));
Data::Hopen->import::into($target, ':all');

# Initialize data in the caller
{
Expand Down
26 changes: 18 additions & 8 deletions lib/App/hopen/T/Gnu/C.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use Class::Tiny qw(op files _cc);

use App::hopen::BuildSystemGlobals; # For $DestDir.
# TODO make the dirs available to nodes through the context.
use App::hopen::Util::BasedPath;
use Config;
use Data::Hopen qw(getparameters);
use Data::Hopen::G::GraphBuilder;
Expand Down Expand Up @@ -102,8 +103,9 @@ sub link {
my ($builder, %args) = getparameters('self', [qw(exe; name)], @_);
croak 'Need the name of the executable' unless $args{exe};

my $dest = based_path(path => file($FN->exe($args{exe})), base => $DestDir);
my $node = __PACKAGE__->new(
op=>'link', files => [$DestDir->file($FN->exe($args{exe}))->absolute],
op=>'link', files => [$dest],
forward_opts(\%args, 'name')
);
hlog { __PACKAGE__, 'Built link node', Dumper($node) } 2;
Expand Down Expand Up @@ -140,20 +142,28 @@ sub _run {
my ($lrFrom, @work);

$lrFrom = deepvalue($lrOldWork, qw(0 to)) // []; # don't autovivify
if($self->op eq 'compile' && $#$lrFrom != 0) {
if($self->op eq 'compile' && @$lrFrom != 1) {
die "C::compile nodes can only take one input filename at present";
# TODO relax this requirement
}

# Add the new work
foreach my $file (@{$lrFrom}) {
my $hr = { from => [ $file ] };
$hr->{to} = [ $self->op eq 'compile' ?
$FN->obj($file) :
$self->files->[0] ];
$hr->{how} = $self->op eq 'compile' ?
$self->_cc . " -c #first -o #out" :
$self->_cc . " #first -o #out";
my ($to, $how);

if($self->op eq 'compile') {
$to = based_path(path => file($FN->obj($file->path)),
base => $DestDir);
$how = $self->_cc . " -c #first -o #out";
} else { # op eq 'link'
$to = $self->files->[0];
$how = $self->_cc . " #first -o #out";
}

$hr->{to} = [ $to ];
$hr->{how} = [ $how ];

push @work, $hr;
$lrFrom = [$file];
}
Expand Down
Loading

0 comments on commit f6c4a85

Please sign in to comment.