line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# App::hopen::Gen - base class for hopen generators |
2
|
|
|
|
|
|
|
package App::hopen::Gen; |
3
|
1
|
|
|
1
|
|
510
|
use Data::Hopen qw(:default $QUIET); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
143
|
|
4
|
1
|
|
|
1
|
|
20
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
25
|
|
5
|
1
|
|
|
1
|
|
6
|
use Data::Hopen::Base; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.000011'; |
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
1390
|
use parent 'Data::Hopen::Visitor'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
10
|
|
|
|
|
|
|
use Class::Tiny qw(proj_dir dest_dir), { |
11
|
|
|
|
|
|
|
architecture => '', |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# private |
14
|
|
|
|
|
|
|
_assets => undef, # A Data::Hopen::G::DAG of the assets |
15
|
2
|
|
|
|
|
32
|
_assetop_by_asset => sub { +{} }, # Indexed by refaddr($asset) |
16
|
1
|
|
|
1
|
|
1965
|
}; |
|
1
|
|
|
|
|
14
|
|
|
1
|
|
|
|
|
9
|
|
17
|
|
|
|
|
|
|
|
18
|
1
|
|
|
1
|
|
901
|
use App::hopen::BuildSystemGlobals; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
121
|
|
19
|
1
|
|
|
1
|
|
10
|
use Data::Hopen::G::DAG; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
42
|
|
20
|
1
|
|
|
1
|
|
6
|
use Data::Hopen::Util::Data qw(forward_opts); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
69
|
|
21
|
1
|
|
|
1
|
|
462
|
use File::pushd qw(pushd); |
|
1
|
|
|
|
|
1252
|
|
|
1
|
|
|
|
|
55
|
|
22
|
1
|
|
|
1
|
|
7
|
use Path::Class (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
20
|
|
23
|
1
|
|
|
1
|
|
5
|
use Scalar::Util qw(refaddr); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
833
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# Docs {{{1 |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 NAME |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Data::Hopen::Gen - Base class for hopen generators |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 SYNOPSIS |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
The code that generates blueprints for specific build systems |
34
|
|
|
|
|
|
|
lives under C<Data::Hopen::Gen>. L<Data::Hopen::Phase::Gen> calls modules |
35
|
|
|
|
|
|
|
under C<Data::Hopen::Gen> to create the blueprints. Those modules must |
36
|
|
|
|
|
|
|
implement the interface defined here. |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head2 proj_dir |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
(Required) A L<Path::Class::Dir> instance specifying the root directory of |
43
|
|
|
|
|
|
|
the project. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head2 dest_dir |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
(Required) A L<Path::Class::Dir> instance specifying where the generated output |
48
|
|
|
|
|
|
|
(e.g., blueprint or other files) should be written. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head2 _assets (Internal) |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
A L<Data::Hopen::G::DAG> of L<App::hopen::G::AssetOp> instances representing |
53
|
|
|
|
|
|
|
the L<App::Hopen::Asset>s to be created when a build is run. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head1 FUNCTIONS |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
A generator (C<Data::Hopen::Gen> subclass) is a Visitor plus some. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
B<Note>: |
60
|
|
|
|
|
|
|
The generator does not have access to L<Data::Hopen::G::Link> instances. |
61
|
|
|
|
|
|
|
That lack of access is the primary distinction between Ops and Links. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# }}}1 |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head2 asset |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Called by an Op (L<App::hopen::G::Op> subclass) to add an asset |
70
|
|
|
|
|
|
|
(L<App::hopen::G::AssetOp> instance) to the build. Usage: |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
$Generator->asset([-asset=>]$asset, [-from=>]$from[, [-how=>]$how]); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
If C<$how> is specified, it will be saved in the C<AssetOp> for use later. |
75
|
|
|
|
|
|
|
Later calls with the same asset and a defined C<$how> will overwrite the |
76
|
|
|
|
|
|
|
C<how> value in the C<AssetOp>. Specify 'UNDEF' as the C<$how> to |
77
|
|
|
|
|
|
|
expressly undefine a C<how>. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Returns the C<AssetOp>. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=cut |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub asset { |
84
|
6
|
|
|
6
|
1
|
50
|
my ($self, %args) = getparameters('self', [qw(asset; how)], @_); |
85
|
6
|
|
|
0
|
|
577
|
hlog { 'Generator adding asset at',refaddr($args{asset}),$args{asset} } 3; |
|
0
|
|
|
|
|
0
|
|
86
|
|
|
|
|
|
|
|
87
|
6
|
|
|
|
|
172
|
my $existing_op = $self->_assetop_by_asset->{refaddr($args{asset})}; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# Update an existing op |
90
|
6
|
50
|
|
|
|
40
|
if(defined $existing_op) { |
91
|
0
|
0
|
0
|
|
|
0
|
if( ($args{how}//'') eq 'UNDEF') { |
|
|
0
|
|
|
|
|
|
92
|
0
|
|
|
|
|
0
|
$existing_op->how(undef); |
93
|
|
|
|
|
|
|
} elsif(defined $args{how}) { |
94
|
0
|
|
|
|
|
0
|
$existing_op->how($args{how}); |
95
|
|
|
|
|
|
|
} |
96
|
0
|
|
|
|
|
0
|
return $existing_op; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Create a new op |
100
|
6
|
|
|
|
|
40
|
my $class = $self->_assetop_class; |
101
|
6
|
|
|
|
|
488
|
eval "require $class"; |
102
|
6
|
|
|
|
|
46
|
my $op = $class->new(name => 'Op:<<' . $args{asset}->target . '>>', |
103
|
|
|
|
|
|
|
forward_opts(\%args, qw(asset how))); |
104
|
6
|
|
|
|
|
173
|
$self->_assetop_by_asset->{refaddr($args{asset})} = $op; |
105
|
6
|
|
|
|
|
146
|
$self->_assets->add($op); |
106
|
6
|
|
|
|
|
3040
|
return $op; |
107
|
|
|
|
|
|
|
} #asset() |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 connect |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Add a dependency edge between two assets or goals. Any assets must have already |
112
|
|
|
|
|
|
|
been added using L</asset>. Usage: |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$Generator->connect([-from=>]$from, [-to=>$to]); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
TODO add missing assets automatically? |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub connect { |
121
|
6
|
|
|
6
|
1
|
28
|
my ($self, %args) = getparameters('self', [qw(from to)], @_); |
122
|
6
|
|
|
|
|
563
|
my %nodes; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# Get the nodes if we were passed assets. |
125
|
6
|
|
|
|
|
15
|
foreach my $field (qw(from to)) { |
126
|
12
|
100
|
|
|
|
64
|
if(eval { $args{$field}->DOES('App::hopen::Asset') }) { |
|
12
|
|
|
|
|
71
|
|
127
|
10
|
|
|
|
|
197
|
$nodes{$field} = $self->_assetop_by_asset->{refaddr($args{$field})}; |
128
|
|
|
|
|
|
|
} else { |
129
|
2
|
|
|
|
|
24
|
$nodes{$field} = $args{$field}; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# TODO better error messages |
134
|
6
|
50
|
|
|
|
472
|
croak "No From node for asset " . refaddr($args{from}) unless $nodes{from}; |
135
|
6
|
50
|
|
|
|
153
|
croak "No To node for asset " . refaddr($args{to}) unless $nodes{to}; |
136
|
6
|
|
|
|
|
204
|
$self->_assets->connect($nodes{from}, $nodes{to}); |
137
|
|
|
|
|
|
|
} #connect() |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head2 run_build |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Runs the build tool for which this generator has created blueprint files. |
142
|
|
|
|
|
|
|
Runs the tool with the destination directory as the current dir. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=cut |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub run_build { |
147
|
0
|
0
|
|
0
|
1
|
0
|
my $self = shift or croak 'Need an instance'; |
148
|
0
|
|
|
|
|
0
|
my $abs_dir = $DestDir->absolute; |
149
|
|
|
|
|
|
|
# NOTE: You have to call this *before* pushd() or chdir(), because |
150
|
|
|
|
|
|
|
# it may be a relative path, and absolute() converts with respect |
151
|
|
|
|
|
|
|
# to cwd at the time of the call. |
152
|
0
|
|
|
|
|
0
|
my $dir = pushd($abs_dir); |
153
|
0
|
0
|
|
|
|
0
|
say "Building in ${abs_dir}..." unless $QUIET; |
154
|
0
|
|
|
|
|
0
|
$self->_run_build(); |
155
|
|
|
|
|
|
|
} #run_build() |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head2 BUILD |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Constructor. |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub BUILD { |
164
|
4
|
|
|
4
|
1
|
528
|
my ($self, $args) = @_; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Enforce the required argument types |
167
|
|
|
|
|
|
|
croak "Need a project directory (Path::Class::Dir)" |
168
|
4
|
50
|
|
|
|
11
|
unless eval { $self->proj_dir->DOES('Path::Class::Dir') }; |
|
4
|
|
|
|
|
110
|
|
169
|
|
|
|
|
|
|
croak "Need a destination directory (Path::Class::Dir)" |
170
|
4
|
50
|
|
|
|
71
|
unless eval { $self->dest_dir->DOES('Path::Class::Dir') }; |
|
4
|
|
|
|
|
81
|
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Create the asset graph |
173
|
4
|
|
|
|
|
55
|
$self->_assets(hnew DAG => 'asset graph'); |
174
|
|
|
|
|
|
|
} #BUILD() |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head1 FUNCTIONS TO BE IMPLEMENTED BY SUBCLASSES |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head2 _assetop_class |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
(Required) Returns the name of the L<App::hopen::G::AssetOp> subclass that |
181
|
|
|
|
|
|
|
should be used to represent assets in the C<_assets> graph. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
184
|
|
|
|
|
|
|
|
185
|
0
|
|
|
0
|
|
|
sub _assetop_class { ... } |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 default_toolset |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
(Required) Returns the package stem of the default toolset for this generator. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
When a hopen file invokes C<use language "Foo">, hopen will load |
192
|
|
|
|
|
|
|
C<< Data::Hopen::T::<stem>::Foo >>, where C<< <stem> >> is the return |
193
|
|
|
|
|
|
|
value of this function. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
As a sanity check, hopen will first try to load C<< Data::Hopen::T::<stem> >>, |
196
|
|
|
|
|
|
|
so make sure that is a valid package. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
199
|
|
|
|
|
|
|
|
200
|
0
|
|
|
0
|
1
|
|
sub default_toolset { ... } |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head2 finalize |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
(Optional) |
205
|
|
|
|
|
|
|
Do whatever the generator wants to do to finish up. By default, no-op. |
206
|
|
|
|
|
|
|
Is provided the L<Data::Hopen::G::DAG> instance as a parameter. Usage: |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$generator->finalize(-phase=>$Phase, -graph=>$dag) |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=cut |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
0
|
1
|
|
sub finalize { } |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=head2 _run_build |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
(Optional) |
217
|
|
|
|
|
|
|
Implementation of L</run_build>. The default does not die, but does warn(). |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=cut |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub _run_build { |
222
|
0
|
|
|
0
|
|
|
warn "This generator is not configured to run a build tool. Sorry!"; |
223
|
|
|
|
|
|
|
} #_run_build() |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head2 visit_goal |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
(Optional) |
228
|
|
|
|
|
|
|
Do whatever the generator wants to do with a L<Data::Hopen::G::Goal>. |
229
|
|
|
|
|
|
|
For example, the generator may change the goal's C<outputs>. |
230
|
|
|
|
|
|
|
By default, no-op. Usage: |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
$generator->visit_goal($goal); |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=cut |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
0
|
1
|
|
sub visit_goal { } |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head2 visit_node |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
(Optional) |
241
|
|
|
|
|
|
|
Do whatever the generator wants to do with a L<Data::Hopen::G::Node> that |
242
|
|
|
|
|
|
|
is not a Goal (see L</visit_goal>). By default, no-op. Usage: |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
$generator->visit_node($node) |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=cut |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
6
|
1
|
|
sub visit_node { } |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
1; |
251
|
|
|
|
|
|
|
__END__ |
252
|
|
|
|
|
|
|
# vi: set fdm=marker: # |