line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# PANT - Perl version of the ANT/NANT building tools.
|
2
|
|
|
|
|
|
|
# Actually not much like them as it doesnt mess with XML currently.
|
3
|
|
|
|
|
|
|
# strike that - it now writes XML, but in an HTML kinda way, well XHTML actually.
|
4
|
|
|
|
|
|
|
package PANT;
|
5
|
|
|
|
|
|
|
|
6
|
7
|
|
|
7
|
|
229348
|
use 5.008;
|
|
7
|
|
|
|
|
27
|
|
|
7
|
|
|
|
|
287
|
|
7
|
7
|
|
|
7
|
|
41
|
use strict;
|
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
251
|
|
8
|
7
|
|
|
7
|
|
38
|
use warnings;
|
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
290
|
|
9
|
7
|
|
|
7
|
|
44
|
use Carp;
|
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
771
|
|
10
|
7
|
|
|
7
|
|
41
|
use Cwd;
|
|
7
|
|
|
|
|
23
|
|
|
7
|
|
|
|
|
2631
|
|
11
|
7
|
|
|
7
|
|
8666
|
use File::Copy;
|
|
7
|
|
|
|
|
50020
|
|
|
7
|
|
|
|
|
540
|
|
12
|
7
|
|
|
7
|
|
9259
|
use File::Copy::Recursive;
|
|
7
|
|
|
|
|
33836
|
|
|
7
|
|
|
|
|
455
|
|
13
|
7
|
|
|
7
|
|
10787
|
use File::Compare ();
|
|
7
|
|
|
|
|
9317
|
|
|
7
|
|
|
|
|
175
|
|
14
|
7
|
|
|
7
|
|
51
|
use File::Basename;
|
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
1030
|
|
15
|
7
|
|
|
7
|
|
6323
|
use File::Spec::Functions qw(:ALL);
|
|
7
|
|
|
|
|
6630
|
|
|
7
|
|
|
|
|
2055
|
|
16
|
7
|
|
|
7
|
|
48
|
use File::Find;
|
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
538
|
|
17
|
7
|
|
|
7
|
|
38
|
use File::Path;
|
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
411
|
|
18
|
7
|
|
|
7
|
|
10114
|
use Getopt::Long;
|
|
7
|
|
|
|
|
116874
|
|
|
7
|
|
|
|
|
54
|
|
19
|
7
|
|
|
7
|
|
10250
|
use XML::Writer;
|
|
7
|
|
|
|
|
154553
|
|
|
7
|
|
|
|
|
263
|
|
20
|
7
|
|
|
7
|
|
7965
|
use IO::File;
|
|
7
|
|
|
|
|
24693
|
|
|
7
|
|
|
|
|
1215
|
|
21
|
7
|
|
|
7
|
|
57
|
use Exporter;
|
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
228
|
|
22
|
7
|
|
|
7
|
|
6853
|
use Digest;
|
|
7
|
|
|
|
|
4055
|
|
|
7
|
|
|
|
|
362
|
|
23
|
7
|
|
|
7
|
|
47
|
use Config;
|
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
30416
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our @ISA = qw(Exporter);
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export
|
28
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead.
|
29
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants.
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# This allows declaration use PANT ':all';
|
32
|
|
|
|
|
|
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
|
33
|
|
|
|
|
|
|
# will save memory.
|
34
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw(
|
35
|
|
|
|
|
|
|
Phase Task NewerThan Command CopyFile CopyFiles DateStamp FileCompare
|
36
|
|
|
|
|
|
|
CopyTree BuildSolution
|
37
|
|
|
|
|
|
|
MoveFile MoveFiles MakeTree RmTree Cvs Svn FindPatternInFile
|
38
|
|
|
|
|
|
|
UpdateFileVersion StartPant EndPant CallPant RunTests Zip) ] );
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
our @EXPORT = ( @{ $EXPORT_TAGS{'all'} } );
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
our $VERSION = '0.17';
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
my $dryrun = 0;
|
48
|
|
|
|
|
|
|
my ($logvolume, $logdirectory, $logfilename, $logstem, $logsuffix);
|
49
|
|
|
|
|
|
|
my $logcount= 1;
|
50
|
|
|
|
|
|
|
my $writer;
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my $this_perl = $^X;
|
53
|
|
|
|
|
|
|
if ($^O ne 'VMS') {
|
54
|
|
|
|
|
|
|
$this_perl .= $Config{_exe}
|
55
|
|
|
|
|
|
|
unless $this_perl =~ m/$Config{_exe}$/i;
|
56
|
|
|
|
|
|
|
}
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 NAME
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
PANT - Perl extension for ANT/NANT like build environments
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
perl buildall.pl -output buildlog
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
use PANT;
|
67
|
|
|
|
|
|
|
StartPant();
|
68
|
|
|
|
|
|
|
Phase(1, "Update");
|
69
|
|
|
|
|
|
|
Task(Command("cvs update"), "Fetch the latest code");
|
70
|
|
|
|
|
|
|
Phase(2, "Build");
|
71
|
|
|
|
|
|
|
Task(UpdateFileVersion("h/version.h",
|
72
|
|
|
|
|
|
|
qr/(#define\s*VERSION\s+)(\d+)/=>q{"$1" . ($2+1)},
|
73
|
|
|
|
|
|
|
"Version file updated");
|
74
|
|
|
|
|
|
|
Task(Command("make all"), "Built distribution");
|
75
|
|
|
|
|
|
|
Phase(3, "Deploy");
|
76
|
|
|
|
|
|
|
Task(Command("make distribution"), "Distribution built");
|
77
|
|
|
|
|
|
|
if (NewerThan(sources=>["myexe"], targets=>["/usr/bin/myexe"])) {
|
78
|
|
|
|
|
|
|
CopyFiles("myexe", "/usr/bin");
|
79
|
|
|
|
|
|
|
}
|
80
|
|
|
|
|
|
|
EndPant();
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head1 ABSTRACT
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
This is a module to help construct automated build environments.
|
85
|
|
|
|
|
|
|
The inspiration came from the ANT/NANT build environments which use
|
86
|
|
|
|
|
|
|
XML to describe a make like syntax of dependencies. For various
|
87
|
|
|
|
|
|
|
reasons none of these were suitable for my purposes, and I suspect
|
88
|
|
|
|
|
|
|
that eventually you will end up writing something pretty similar to
|
89
|
|
|
|
|
|
|
perl in XML to cater for all the things you want to do. Also a
|
90
|
|
|
|
|
|
|
module named PANT was just too good a name to miss!
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
This module draws on some of the ideas in ANT/NANT, and also in the
|
93
|
|
|
|
|
|
|
Test::Mode module for ways to do things. This module is therefore a
|
94
|
|
|
|
|
|
|
collection of tools to help automate processes, and provide a build
|
95
|
|
|
|
|
|
|
log of what happened, so remote builds can be observed.
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
The basic philosophy is that you can probably use make or visual studio
|
98
|
|
|
|
|
|
|
or similar to do the heavy building. There is no real need to replicate
|
99
|
|
|
|
|
|
|
that. However stuff like checking out of CVS/SVN repositories, updating
|
100
|
|
|
|
|
|
|
version numbers, checking it back in, running test harnesses, and similar
|
101
|
|
|
|
|
|
|
are things that make is not good at.
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
XML is not a programming language, but you can describe a lot of
|
104
|
|
|
|
|
|
|
what you want using it, which is what ANT/NANT basically do. However
|
105
|
|
|
|
|
|
|
there is always something you want to do, which can't be described
|
106
|
|
|
|
|
|
|
in the current description language. In these cases you can call out
|
107
|
|
|
|
|
|
|
to an external routine to do things.
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
However it seems much easier to provide a number of useful
|
110
|
|
|
|
|
|
|
subroutines in a scripting language, which help you build
|
111
|
|
|
|
|
|
|
things. Then if you need to do something slightly of piste, you have
|
112
|
|
|
|
|
|
|
all the power right there.
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
The other thing I want to know about is "did it work" and if it
|
115
|
|
|
|
|
|
|
didn't, what went wrong? To this end plenty of logging is required so
|
116
|
|
|
|
|
|
|
the build can be tracked. As the build is probably going to be remote,
|
117
|
|
|
|
|
|
|
HTML seems the obvious choice to report in, so you can just look at it
|
118
|
|
|
|
|
|
|
from a web server.
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
This module provides various useful functions to help in the automated
|
123
|
|
|
|
|
|
|
build of a project and to produce a build log. It is still in
|
124
|
|
|
|
|
|
|
development, and may well change shape in the light of experience.
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 EXPORTS
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head2 StartPant([title],[style=>stuff])
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
This call should be the first call into the module. It does some
|
131
|
|
|
|
|
|
|
intialisation, and parses command line arguments in @ARGV. It takes
|
132
|
|
|
|
|
|
|
the following arguments.
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=over 4
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item String
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
The first argument is a string, and is used as the title of the web page if present.
|
139
|
|
|
|
|
|
|
If not present it will be called "Build Log".
|
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item style=>stuff
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
This argument if present signals some style data to include. This will
|
144
|
|
|
|
|
|
|
be included in a EstyleE tag. This allows you to apply different styles to
|
145
|
|
|
|
|
|
|
the generated page.
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item stylelink=>href
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
This argument if present directs the inclusion of a style sheet external link.
|
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=back
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Supported command line options are
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=over 4
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=item -output file
|
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
Write the output to the given file.
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item -dryrun
|
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Simulate a run without actually doing anything.
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=back
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=cut
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub StartPant {
|
171
|
11
|
|
100
|
11
|
1
|
13707
|
my $title = shift || "Build log";
|
172
|
11
|
|
|
|
|
65
|
my(%extra) = @_;
|
173
|
11
|
|
|
|
|
41
|
my $logname = "";
|
174
|
11
|
|
|
|
|
111
|
GetOptions("output=s"=>\$logname,
|
175
|
|
|
|
|
|
|
n=>\$dryrun,
|
176
|
|
|
|
|
|
|
dryrun=>\$dryrun);
|
177
|
11
|
|
|
|
|
6299
|
my $fh;
|
178
|
11
|
50
|
|
|
|
47
|
if ($logname) {
|
179
|
11
|
50
|
|
|
|
150
|
$fh = new IO::File "$logname", "w" or die "Can't open file $logname: $!";
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
}
|
182
|
|
|
|
|
|
|
else {
|
183
|
0
|
|
|
|
|
0
|
$logname = "buildlog.html";
|
184
|
0
|
0
|
|
|
|
0
|
open $fh, ">&STDOUT" or die "Can't duplicate stdout: $!";
|
185
|
|
|
|
|
|
|
}
|
186
|
11
|
100
|
|
|
|
3408
|
if (file_name_is_absolute($logname)) {
|
187
|
1
|
|
|
|
|
18
|
($logvolume,$logdirectory,$logfilename) = splitpath( $logname );
|
188
|
|
|
|
|
|
|
}
|
189
|
|
|
|
|
|
|
else {
|
190
|
10
|
|
|
|
|
263
|
($logvolume,$logdirectory,$logfilename) = splitpath(catfile(getcwd, $logname));
|
191
|
|
|
|
|
|
|
}
|
192
|
11
|
|
|
|
|
276
|
$logstem = $logfilename;
|
193
|
11
|
|
|
|
|
119
|
$logstem =~ s/(\.[^.]+)$//;
|
194
|
11
|
|
|
|
|
29
|
$logsuffix = $1;
|
195
|
11
|
|
|
|
|
240
|
$writer = XML::Writer->new(NEWLINES=>1, OUTPUT=>$fh);
|
196
|
11
|
|
|
|
|
8661
|
$writer->xmlDecl();
|
197
|
11
|
|
|
|
|
1210
|
$writer->doctype('html', "-//W3C//DTD XHTML 1.0 Transitional//EN", "http://www.w3.org/TR/xhtml1/DTD/transitional.dtd");
|
198
|
11
|
|
|
|
|
440
|
$writer->startTag('html', xmlns=>"http://www/w3/org/TR/xhtml1");
|
199
|
11
|
|
|
|
|
1043
|
$writer->startTag('head');
|
200
|
11
|
|
|
|
|
792
|
$writer->dataElement('title', $title);
|
201
|
11
|
50
|
|
|
|
1122
|
if ($extra{stylelink}) {
|
202
|
0
|
|
|
|
|
0
|
$writer->emptyTag('link', href=>$extra{stylelink}, type=>"text/css");
|
203
|
|
|
|
|
|
|
}
|
204
|
11
|
50
|
|
|
|
44
|
if ($extra{style}) {
|
205
|
0
|
|
|
|
|
0
|
$writer->dataElement('style', $extra{style}, type=>"text/css");
|
206
|
|
|
|
|
|
|
}
|
207
|
11
|
|
|
|
|
46
|
$writer->endTag('head');
|
208
|
11
|
|
|
|
|
316
|
$writer->startTag('body');
|
209
|
|
|
|
|
|
|
}
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head2 EndPant()
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
This function finishes up the run, and should be the last call into
|
214
|
|
|
|
|
|
|
the module. It completes the build log in a tidy way.
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=cut
|
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub EndPant {
|
219
|
10
|
100
|
|
10
|
1
|
38003
|
$writer->endTag('ul') if $writer->in_element('ul');
|
220
|
10
|
|
|
|
|
424
|
$writer->endTag('body');
|
221
|
10
|
|
|
|
|
279
|
$writer->endTag('html');
|
222
|
10
|
|
|
|
|
279
|
$writer->end();
|
223
|
10
|
|
|
|
|
312
|
undef $writer; # close files and flush
|
224
|
|
|
|
|
|
|
}
|
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head2 CallPant(name, options)
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
This function allows you to call a subsidiary pant build. The build
|
230
|
|
|
|
|
|
|
will be run and waited for. A reference in the current log will be
|
231
|
|
|
|
|
|
|
made to the new log. It is assumed that the subsidiary build is also
|
232
|
|
|
|
|
|
|
using PANT as it passes some command line arguments to sort out the
|
233
|
|
|
|
|
|
|
logging.
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Options include
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=over 4
|
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=item directory=>place
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Change to the given directory to run the subsidiary build. The log
|
242
|
|
|
|
|
|
|
path should be modified so it fits.
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=item logname=>name
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
Name the log file that it will write to this. If this is not given, a
|
247
|
|
|
|
|
|
|
name will be made up for you.
|
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=back
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=cut
|
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# call a subsidiary build
|
254
|
|
|
|
|
|
|
sub CallPant {
|
255
|
4
|
|
|
4
|
1
|
350
|
my $build = shift;
|
256
|
4
|
|
|
|
|
21
|
my (%args) = @_;
|
257
|
4
|
|
|
|
|
15
|
$writer->startTag('li');
|
258
|
|
|
|
|
|
|
|
259
|
4
|
|
|
|
|
337
|
$writer->characters("Calling subsidiary build $build.");
|
260
|
4
|
100
|
|
|
|
95
|
my $dir = exists $args{directory} ? $args{directory} : ".";
|
261
|
4
|
|
66
|
|
|
22
|
my $logthisname = $args{logname} || "$logstem-$logcount$logsuffix";
|
262
|
4
|
100
|
|
|
|
24
|
$logthisname .= $logsuffix if ($logthisname !~ /\.[^.]+/);
|
263
|
4
|
|
|
|
|
118
|
my $logfile = catpath($logvolume, $logdirectory, $logthisname);
|
264
|
4
|
|
|
|
|
58
|
my $relfile = abs2rel($logfile, $dir);
|
265
|
4
|
|
|
|
|
646
|
my $rv = Command("$this_perl $build -output $relfile",
|
266
|
|
|
|
|
|
|
log=>$logfile, @_);
|
267
|
4
|
|
|
|
|
11
|
$logcount ++;
|
268
|
4
|
|
|
|
|
18
|
$writer->endTag('li');
|
269
|
4
|
|
|
|
|
343
|
return $rv;
|
270
|
|
|
|
|
|
|
}
|
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=head2 Phase([list])
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
This function is purely for help in dividing up the build log. It
|
275
|
|
|
|
|
|
|
inserts a heading into the log allowing you to divide the build up
|
276
|
|
|
|
|
|
|
into a variety of parts. You might have a pre-build cvs checkput
|
277
|
|
|
|
|
|
|
phase, a build phase, and followed up by a test and deployment phase.
|
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
The list is used as the contents of the header, and the first element
|
280
|
|
|
|
|
|
|
of the list is used as an HTML anchor in case you want to refer to it.
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=cut
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# a phase marker, for dividing up output a bit
|
285
|
|
|
|
|
|
|
sub Phase {
|
286
|
0
|
0
|
|
0
|
1
|
0
|
$writer->endTag('ul') if $writer->in_element('ul');
|
287
|
0
|
|
|
|
|
0
|
$writer->startTag('a', name=>$_[0]);
|
288
|
0
|
|
|
|
|
0
|
$writer->startTag('h1');
|
289
|
0
|
|
|
|
|
0
|
$writer->characters("@_");
|
290
|
0
|
|
|
|
|
0
|
$writer->endTag('h1');
|
291
|
0
|
|
|
|
|
0
|
$writer->endTag('a');
|
292
|
0
|
|
|
|
|
0
|
$writer->startTag('ul');
|
293
|
|
|
|
|
|
|
}
|
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head2 DateStamp
|
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
This function returns a datestamp in a common format. Its is intended
|
298
|
|
|
|
|
|
|
for use in logging output, and also in CVS/SVN type retrievals.
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
## cvs like date/time
|
303
|
|
|
|
|
|
|
sub DateStamp {
|
304
|
0
|
|
|
0
|
1
|
0
|
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
|
305
|
0
|
|
|
|
|
0
|
$year += 1900;
|
306
|
0
|
|
|
|
|
0
|
$mon++;
|
307
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
return "$year-$mon-$mday $hour:$min:$sec";
|
309
|
|
|
|
|
|
|
}
|
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head2 NewerThan(sources=>[qw(f1 f2*.txt)], targets=>[build], ...)
|
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
This function provides a make like dependency checker.
|
315
|
|
|
|
|
|
|
It has the following arguments,
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=over 4
|
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item sources
|
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
A list of wildcard (glob'able) files that are the source.
|
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=item treesources
|
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
A list of wildcard directories that are descended for source files.
|
326
|
|
|
|
|
|
|
Currently all files in the tree are considered possibilities.
|
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=item targets
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
A list of wildcard (glob'able) files that are the target
|
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=back
|
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
The function will return true if any of the sources are
|
335
|
|
|
|
|
|
|
newer than the oldest of the targets.
|
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=cut
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# compares sources and targets
|
340
|
|
|
|
|
|
|
# Pick oldest of the targets
|
341
|
|
|
|
|
|
|
# newest of the sources.
|
342
|
|
|
|
|
|
|
sub NewerThan {
|
343
|
2
|
|
|
2
|
1
|
14
|
my (%args) = @_;
|
344
|
2
|
100
|
|
|
|
14
|
$writer->startTag('ul') if ! $writer->in_element('ul');
|
345
|
2
|
|
|
|
|
138
|
$writer->startTag('li');
|
346
|
2
|
|
|
|
|
80
|
my $srcs = "";
|
347
|
2
|
50
|
|
|
|
7
|
$srcs .= " the files @{ $args{sources} }" if exists $args{sources};
|
|
2
|
|
|
|
|
7
|
|
348
|
2
|
50
|
|
|
|
6
|
$srcs .= " the directories @{ $args{treesources} }" if exists $args{treesources};
|
|
0
|
|
|
|
|
0
|
|
349
|
2
|
|
|
|
|
6
|
$writer->characters("Are any of $srcs newer than @{ $args{targets} }? ");
|
|
2
|
|
|
|
|
10
|
|
350
|
2
|
|
|
|
|
39
|
my $newestt = time;
|
351
|
2
|
|
|
|
|
6
|
my $tfile = "none";
|
352
|
2
|
|
|
|
|
3
|
foreach my $glob (@{ $args{targets} }) {
|
|
2
|
|
|
|
|
5
|
|
353
|
2
|
|
|
|
|
77
|
foreach my $sfile (glob $glob) {
|
354
|
|
|
|
|
|
|
|
355
|
2
|
|
|
|
|
35
|
my $t = (stat($sfile))[9];
|
356
|
2
|
50
|
|
|
|
9
|
if ($t) {
|
357
|
2
|
100
|
|
|
|
6
|
$newestt = $t if ($t < $newestt);
|
358
|
2
|
|
|
|
|
10
|
$tfile = $sfile;
|
359
|
|
|
|
|
|
|
}
|
360
|
|
|
|
|
|
|
else {
|
361
|
0
|
|
|
|
|
0
|
$writer->dataElement('li', "Warning: $sfile doesn't exist\n");
|
362
|
0
|
|
|
|
|
0
|
$newestt = 0;
|
363
|
|
|
|
|
|
|
}
|
364
|
|
|
|
|
|
|
}
|
365
|
|
|
|
|
|
|
}
|
366
|
2
|
|
|
|
|
73
|
my $newests = 1;
|
367
|
2
|
|
|
|
|
7
|
my $srcfile = "none";
|
368
|
2
|
50
|
|
|
|
6
|
if ($newestt > 0) {
|
369
|
2
|
|
|
|
|
3
|
GLOB: foreach my $glob (@{ $args{sources} }) {
|
|
2
|
|
|
|
|
6
|
|
370
|
2
|
|
|
|
|
29
|
foreach my $sfile (glob $glob) {
|
371
|
2
|
|
|
|
|
27
|
my $t = (stat($sfile))[9];
|
372
|
2
|
50
|
|
|
|
7
|
if ($t) {
|
373
|
2
|
50
|
|
|
|
9
|
if ($t > $newests) {
|
374
|
2
|
|
|
|
|
6
|
$srcfile = $sfile;
|
375
|
2
|
|
|
|
|
3
|
$newests = $t
|
376
|
|
|
|
|
|
|
}
|
377
|
2
|
100
|
|
|
|
12
|
last GLOB if ($newests > $newestt);
|
378
|
|
|
|
|
|
|
}
|
379
|
|
|
|
|
|
|
else {
|
380
|
0
|
|
|
|
|
0
|
carp "$sfile doesn't exist\n";
|
381
|
0
|
0
|
|
|
|
0
|
Abort("$sfile doesn't exist\n") if ($args{dieonerror});
|
382
|
0
|
|
|
|
|
0
|
$newests = 0;
|
383
|
|
|
|
|
|
|
}
|
384
|
|
|
|
|
|
|
}
|
385
|
|
|
|
|
|
|
}
|
386
|
|
|
|
|
|
|
my $wanted = sub {
|
387
|
0
|
|
|
0
|
|
0
|
my $t = (stat($_))[9];
|
388
|
0
|
0
|
|
|
|
0
|
if ($t > $newestt) {
|
389
|
0
|
|
|
|
|
0
|
$srcfile = $_;
|
390
|
0
|
|
|
|
|
0
|
$newests = $t;
|
391
|
|
|
|
|
|
|
}
|
392
|
2
|
|
|
|
|
16
|
};
|
393
|
2
|
|
|
|
|
3
|
foreach my $glob (@{ $args{treesources} }) {
|
|
2
|
|
|
|
|
12
|
|
394
|
0
|
|
|
|
|
0
|
foreach my $sfile (glob $glob) {
|
395
|
0
|
|
|
|
|
0
|
find($wanted, $sfile);
|
396
|
|
|
|
|
|
|
#print "Check tree $sfile\n";
|
397
|
|
|
|
|
|
|
}
|
398
|
|
|
|
|
|
|
}
|
399
|
|
|
|
|
|
|
}
|
400
|
2
|
|
|
|
|
4
|
my $rval = $newests > $newestt;
|
401
|
2
|
100
|
|
|
|
12
|
$writer->characters($rval ? "Yes" : "No");
|
402
|
2
|
|
|
|
|
35
|
$writer->endTag('li');
|
403
|
|
|
|
|
|
|
# print "Source $srcfile ", scalar(localtime($newests)), " Dest $tfile ", scalar(localtime($newestt)), " $rval\n";
|
404
|
2
|
|
|
|
|
43
|
return $rval;
|
405
|
|
|
|
|
|
|
}
|
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=head2 Task(result, message)
|
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
This command evaluates the first argument to see if it is true, and
|
410
|
|
|
|
|
|
|
prints the second argument into the log. If the first argument is
|
411
|
|
|
|
|
|
|
false, the build will abort.
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=cut
|
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# checks a task succeeded
|
416
|
|
|
|
|
|
|
sub Task {
|
417
|
2
|
|
|
2
|
1
|
46
|
my $test = shift;
|
418
|
2
|
|
|
|
|
11
|
$writer->dataElement('li', "@_\n");
|
419
|
2
|
50
|
|
|
|
159
|
Abort("FAILED: ", @_) if ! $test;
|
420
|
2
|
|
|
|
|
13
|
return 1;
|
421
|
|
|
|
|
|
|
}
|
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head2 Abort(list)
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
This function aborts the build and is called internally when thigns go
|
426
|
|
|
|
|
|
|
wrong.
|
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=cut
|
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# give up and go home
|
431
|
|
|
|
|
|
|
sub Abort {
|
432
|
0
|
|
|
0
|
1
|
0
|
$writer->dataElement('span', Carp::longmess("@_"),
|
433
|
|
|
|
|
|
|
style=>"color:red;font-weight:bold");
|
434
|
0
|
|
|
|
|
0
|
EndPant();
|
435
|
0
|
|
|
|
|
0
|
confess @_;
|
436
|
|
|
|
|
|
|
}
|
437
|
|
|
|
|
|
|
=head2 Command(cmd, options)
|
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
This function runs the given external command, capturing the output
|
440
|
|
|
|
|
|
|
for the log, and evaluating the return code to see if it worked.
|
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=over 4
|
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
Currently there is only one option
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item directory=>"somewhere"
|
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
This will cause the command to run in the given directory, rather than
|
449
|
|
|
|
|
|
|
being where you happen to be currently.
|
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=back
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=cut
|
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# run a command, in a directory maybe
|
456
|
|
|
|
|
|
|
sub Command {
|
457
|
5
|
|
|
5
|
0
|
10
|
my $cmd = shift;
|
458
|
5
|
|
|
|
|
19
|
my (%args) = @_;
|
459
|
5
|
|
|
|
|
10
|
my $cdir = ".";
|
460
|
5
|
100
|
|
|
|
24
|
if ($args{directory}) {
|
461
|
3
|
|
|
|
|
19
|
$cdir = getcwd;
|
462
|
3
|
50
|
|
|
|
73
|
chdir($args{directory}) || Abort("Can't change to directory $args{directory}");
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
}
|
465
|
5
|
|
|
|
|
21
|
$writer->startTag('li');
|
466
|
5
|
|
|
|
|
225
|
$writer->characters("Run $cmd\n");
|
467
|
5
|
|
|
|
|
170
|
my $output;
|
468
|
|
|
|
|
|
|
my $retval;
|
469
|
5
|
50
|
|
|
|
16
|
if ($dryrun) {
|
470
|
0
|
|
|
|
|
0
|
$output = "Output of the command $cmd would be here";
|
471
|
0
|
|
|
|
|
0
|
$retval = 1;
|
472
|
|
|
|
|
|
|
}
|
473
|
|
|
|
|
|
|
else {
|
474
|
5
|
|
|
|
|
20
|
$writer->startTag('pre');
|
475
|
5
|
|
|
|
|
185
|
$cmd .= " 2>&1"; # collect stderr too
|
476
|
5
|
50
|
|
|
|
45236
|
if (open(PIPE, "$cmd |")) {
|
477
|
5
|
|
|
|
|
846392
|
while(my $line = ) {
|
478
|
1
|
|
|
|
|
57
|
$writer->characters($line);
|
479
|
|
|
|
|
|
|
}
|
480
|
5
|
|
|
|
|
571
|
close(PIPE);
|
481
|
5
|
|
|
|
|
86
|
$retval = $? == 0;
|
482
|
|
|
|
|
|
|
}
|
483
|
|
|
|
|
|
|
else {
|
484
|
0
|
|
|
|
|
0
|
$retval = 0;
|
485
|
|
|
|
|
|
|
}
|
486
|
5
|
|
|
|
|
528
|
$writer->endTag('pre');
|
487
|
|
|
|
|
|
|
}
|
488
|
5
|
50
|
|
|
|
413
|
$writer->characters("$cmd failed: $!") if ($retval == 0);
|
489
|
5
|
100
|
|
|
|
32
|
if ($args{log}) {
|
490
|
4
|
|
|
|
|
75
|
my($v,$d, $f) = splitpath($args{log});
|
491
|
4
|
|
|
|
|
521
|
my $fulllog = rel2abs($args{log});
|
492
|
4
|
|
|
|
|
201
|
my $destlog = catpath($logvolume, $logdirectory, $f);
|
493
|
4
|
50
|
|
|
|
89
|
CopyFile($args{log}, $destlog) if($fulllog ne $destlog);
|
494
|
4
|
|
|
|
|
17
|
my $reldir = abs2rel($args{log}, catpath($logvolume, $logdirectory, ''));
|
495
|
|
|
|
|
|
|
|
496
|
4
|
|
|
|
|
783
|
$writer->dataElement('a', "Log file", href=>$f);
|
497
|
|
|
|
|
|
|
}
|
498
|
5
|
|
|
|
|
1414
|
$writer->endTag('li');
|
499
|
5
|
50
|
|
|
|
143
|
do { chdir($cdir) || Abort("Can't change back to $cdir: $!"); } if ($args{directory});
|
|
3
|
100
|
|
|
|
114
|
|
500
|
5
|
|
|
|
|
117
|
return $retval;
|
501
|
|
|
|
|
|
|
}
|
502
|
|
|
|
|
|
|
=head2 CopyFiles(source, destdir)
|
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
This function copies all the files that match the source glob pattern
|
505
|
|
|
|
|
|
|
to the given directory. The names will remain the same.
|
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=cut
|
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# copy over several files into a new directory
|
510
|
|
|
|
|
|
|
sub CopyFiles {
|
511
|
0
|
|
|
0
|
0
|
0
|
my ($src, $dest) = @_;
|
512
|
0
|
0
|
0
|
|
|
0
|
Abort("$dest is not a directory") if (!$dryrun && ! -d $dest);
|
513
|
0
|
|
|
|
|
0
|
foreach my $sfile (glob $src) {
|
514
|
0
|
|
|
|
|
0
|
my $bname = basename($sfile);
|
515
|
0
|
0
|
|
|
|
0
|
return 0 if CopyFile($sfile, "$dest/$bname") == 0;
|
516
|
|
|
|
|
|
|
}
|
517
|
0
|
|
|
|
|
0
|
return 1;
|
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
}
|
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=head2 CopyTree(source, dest)
|
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
This function copies an entire tree hierarchy from the source to the
|
524
|
|
|
|
|
|
|
destination. It makes use of File::Copy::Recursive routines to do this.
|
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=cut
|
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# copy over several files into a new directory
|
529
|
|
|
|
|
|
|
sub CopyTree {
|
530
|
1
|
|
|
1
|
1
|
3
|
my ($src, $dest) = @_;
|
531
|
1
|
|
|
|
|
8
|
$writer->dataElement('li', "Copy $src tree to $dest\n");
|
532
|
1
|
50
|
|
|
|
81
|
if (!$dryrun) {
|
533
|
1
|
|
|
|
|
13
|
my($nfdirs,$ndirs,$depth) = File::Copy::Recursive::rcopy($src, $dest);
|
534
|
1
|
|
|
|
|
1095
|
$writer->dataElement('li',
|
535
|
|
|
|
|
|
|
"Copied $nfdirs files and directories, $ndirs directories to a depth of $depth");
|
536
|
|
|
|
|
|
|
|
537
|
1
|
|
|
|
|
153
|
return $nfdirs;
|
538
|
|
|
|
|
|
|
}
|
539
|
0
|
|
|
|
|
0
|
return 1;
|
540
|
|
|
|
|
|
|
}
|
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head2 CopyFile(source, dest)
|
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
This function copies an individual file from the source to the
|
545
|
|
|
|
|
|
|
destination. It allows for renaming.
|
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=cut
|
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# copy a file and possibly rename.
|
550
|
|
|
|
|
|
|
sub CopyFile {
|
551
|
2
|
|
|
2
|
1
|
5
|
my ($src, $dest) = @_;
|
552
|
2
|
|
|
|
|
12
|
$writer->dataElement('li', "Copy $src to $dest\n");
|
553
|
2
|
50
|
|
|
|
152
|
return 1 if ($dryrun);
|
554
|
2
|
100
|
|
|
|
16
|
if( copy($src, $dest) == 0) {
|
555
|
1
|
|
|
|
|
240
|
$writer->dataElement('li', "Copy failed: $!\n");
|
556
|
1
|
|
|
|
|
65
|
return 0;
|
557
|
|
|
|
|
|
|
}
|
558
|
1
|
|
|
|
|
13087
|
return 1;
|
559
|
|
|
|
|
|
|
}
|
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=head2 MoveFiles(source, destdir)
|
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
This function moves all the files that match the source glob pattern
|
565
|
|
|
|
|
|
|
to the given directory. The names will remain the same.
|
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=cut
|
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# move over several files into a new directory
|
570
|
|
|
|
|
|
|
sub MoveFiles {
|
571
|
0
|
|
|
0
|
1
|
0
|
my ($src, $dest) = @_;
|
572
|
0
|
0
|
0
|
|
|
0
|
Abort("$dest is not a directory") if (!$dryrun && ! -d $dest);
|
573
|
0
|
|
|
|
|
0
|
foreach my $sfile (glob $src) {
|
574
|
0
|
|
|
|
|
0
|
my $bname = basename($sfile);
|
575
|
0
|
0
|
|
|
|
0
|
return 0 if MoveFile($sfile, "$dest/$bname") == 0;
|
576
|
|
|
|
|
|
|
}
|
577
|
0
|
|
|
|
|
0
|
return 1;
|
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
}
|
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head2 MoveFile(source, dest)
|
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
This function moves an individual file from the source to the
|
584
|
|
|
|
|
|
|
destination. It allows for renaming.
|
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=cut
|
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# copy a file and possibly rename.
|
589
|
|
|
|
|
|
|
sub MoveFile {
|
590
|
1
|
|
|
1
|
1
|
3
|
my ($src, $dest) = @_;
|
591
|
1
|
|
|
|
|
18
|
$writer->dataElement('li', "Move $src to $dest\n");
|
592
|
1
|
50
|
|
|
|
126
|
return 1 if ($dryrun);
|
593
|
1
|
50
|
|
|
|
12
|
if( move($src, $dest) == 0) {
|
594
|
0
|
|
|
|
|
0
|
$writer->dataElement('li', "Move failed: $!\n");
|
595
|
0
|
|
|
|
|
0
|
return 0;
|
596
|
|
|
|
|
|
|
}
|
597
|
1
|
|
|
|
|
129
|
return 1;
|
598
|
|
|
|
|
|
|
}
|
599
|
|
|
|
|
|
|
=head2 UpdateFileVersion(file, patterns)
|
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
This functions name will probably change. It allows for updating files
|
602
|
|
|
|
|
|
|
contents based on the given set of patterns. Some care is needed to
|
603
|
|
|
|
|
|
|
get the patterns and the replacements correct. The replacement text is
|
604
|
|
|
|
|
|
|
subject to double evaluation.
|
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=cut
|
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
sub UpdateFileVersion {
|
609
|
2
|
|
|
2
|
0
|
424
|
my ($file, %patterns) = @_;
|
610
|
2
|
100
|
|
|
|
14
|
$writer->startTag('ul') if ! $writer->in_element('ul');
|
611
|
2
|
|
|
|
|
80
|
$writer->startTag('li');
|
612
|
2
|
|
|
|
|
114
|
$writer->characters("Update file $file\n");
|
613
|
2
|
|
|
|
|
58
|
$writer->startTag('ul');
|
614
|
2
|
50
|
|
|
|
154
|
open(FILE, $file) || do { $writer->characters("Can't open file $file: $!"); return 0; };
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
615
|
2
|
50
|
|
|
|
290
|
open(FILEOUT, ">$file.$$") || do { $writer->characters("Can't open file $file.$$: $!"); return 0; };
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
616
|
2
|
|
|
|
|
44
|
while (my $line = ) {
|
617
|
8
|
|
|
|
|
32
|
while( my($k, $v) = each %patterns) {
|
618
|
12
|
100
|
|
|
|
329
|
if ($line =~ s/$k/$v/ee) {
|
|
3
|
|
|
|
|
314
|
|
619
|
3
|
|
|
|
|
5
|
my $vv;
|
620
|
3
|
|
|
|
|
183
|
eval "\$vv = $v";
|
621
|
3
|
|
|
|
|
545
|
$writer->dataElement("li","Changed line '$line' '$1' '$2' '$v' $vv\n");
|
622
|
|
|
|
|
|
|
}
|
623
|
|
|
|
|
|
|
}
|
624
|
8
|
|
|
|
|
352
|
print FILEOUT $line;
|
625
|
|
|
|
|
|
|
}
|
626
|
2
|
|
|
|
|
26
|
close(FILE);
|
627
|
2
|
|
|
|
|
217
|
close(FILEOUT);
|
628
|
2
|
|
|
|
|
11
|
$writer->endTag('ul');
|
629
|
2
|
|
|
|
|
54
|
$writer->endTag('li');
|
630
|
2
|
50
|
|
|
|
52
|
return 1 if ($dryrun);
|
631
|
2
|
|
|
|
|
276
|
return rename("$file.$$", $file);
|
632
|
|
|
|
|
|
|
}
|
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
=head2 AddOutput(list)
|
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
This allows additional commentary to be added to the output stream.
|
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=cut
|
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
sub AddOutput {
|
642
|
0
|
|
|
0
|
1
|
0
|
$writer->characters("@_");
|
643
|
|
|
|
|
|
|
}
|
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head2 AddElement(list)
|
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
This allows additional constructs to be added to the output, such a
|
648
|
|
|
|
|
|
|
href references and so on. It is passed onto XML::Writer::dataElement
|
649
|
|
|
|
|
|
|
directly and takes the same syntax.
|
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=cut
|
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub AddElement {
|
654
|
0
|
|
|
0
|
1
|
0
|
$writer->dataElement(@_);
|
655
|
|
|
|
|
|
|
}
|
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=head2 RunTests(args)
|
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
Run the list of perl style test files, and capture the result in the
|
661
|
|
|
|
|
|
|
output of the log. The The arguments allow you to specify the tests to
|
662
|
|
|
|
|
|
|
run, see PANT::Test for details.
|
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=cut
|
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
sub RunTests {
|
667
|
1
|
|
|
1
|
1
|
42
|
require PANT::Test;
|
668
|
1
|
|
|
|
|
11
|
my $test = new PANT::Test($writer, dryrun=>$dryrun);
|
669
|
1
|
|
|
|
|
4
|
return $test->RunTests(@_);
|
670
|
|
|
|
|
|
|
}
|
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head2 Zip(file)
|
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
This function returns a PANT::Zip object to help construct the given zip file.
|
675
|
|
|
|
|
|
|
See PANT::Zip for more details.
|
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=cut
|
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
sub Zip {
|
680
|
1
|
|
|
1
|
1
|
47
|
require PANT::Zip;
|
681
|
1
|
|
|
|
|
11
|
return new PANT::Zip($writer, @_, dryrun=>$dryrun);
|
682
|
|
|
|
|
|
|
}
|
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=head2 Cvs()
|
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
This function returns a PANT::Cvs object to help with running Cvs commands.
|
687
|
|
|
|
|
|
|
See PANT::Cvs for more details.
|
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=cut
|
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
sub Cvs {
|
692
|
1
|
|
|
1
|
1
|
228
|
require PANT::Cvs;
|
693
|
1
|
|
|
|
|
10
|
return new PANT::Cvs($writer, @_, dryrun=>$dryrun);
|
694
|
|
|
|
|
|
|
}
|
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=head2 Svn()
|
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
This function returns a PANT::Svn object to help with running Svn commands.
|
699
|
|
|
|
|
|
|
See PANT::Svn for more details.
|
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=cut
|
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
sub Svn {
|
704
|
1
|
|
|
1
|
1
|
264
|
require PANT::Svn;
|
705
|
1
|
|
|
|
|
11
|
return new PANT::Svn($writer, @_, dryrun=>$dryrun);
|
706
|
|
|
|
|
|
|
}
|
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
=head2 FileCompare(F1, F2)
|
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
This function compares two files using the File::Compare routines to
|
712
|
|
|
|
|
|
|
see if their contents are identical.
|
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=cut
|
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
sub FileCompare {
|
717
|
1
|
|
|
1
|
1
|
3
|
my($f1, $f2) = @_;
|
718
|
1
|
|
|
|
|
16
|
return File::Compare::compare($f1, $f2) == 0;
|
719
|
|
|
|
|
|
|
}
|
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=head2 MakeTree(dir)
|
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
Create a given directory, and all required intermediate paths.
|
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=cut
|
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
sub MakeTree {
|
728
|
1
|
|
|
1
|
1
|
1552
|
my $dir = shift;
|
729
|
1
|
|
|
|
|
11
|
$writer->dataElement('li', "Create directory tree $dir\n");
|
730
|
1
|
50
|
|
|
|
244
|
return 1 if ($dryrun);
|
731
|
1
|
|
|
|
|
66
|
eval { mkpath($dir) };
|
|
1
|
|
|
|
|
438
|
|
732
|
1
|
50
|
|
|
|
5
|
if ($@) {
|
733
|
0
|
|
|
|
|
0
|
$writer->dataElement('li', "Couldn't create directory $dir: $@");
|
734
|
0
|
|
|
|
|
0
|
return 0;
|
735
|
|
|
|
|
|
|
}
|
736
|
1
|
|
|
|
|
17
|
return -d $dir;
|
737
|
|
|
|
|
|
|
}
|
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
=head2 RmTree(dir)
|
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
This function removes the entire tree starting at the given directory.
|
742
|
|
|
|
|
|
|
Obviously be careful!
|
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
=cut
|
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
sub RmTree {
|
747
|
2
|
|
|
2
|
1
|
9
|
my $dir = shift;
|
748
|
2
|
|
|
|
|
12
|
$writer->dataElement('li', "Remove tree $dir\n");
|
749
|
2
|
50
|
|
|
|
189
|
return 1 if ($dryrun);
|
750
|
2
|
|
|
|
|
4023
|
rmtree($dir);
|
751
|
2
|
|
|
|
|
36
|
return ! -d $dir;
|
752
|
|
|
|
|
|
|
}
|
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=head2 BuildSolution(project, args...)
|
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
This function attempts to build a visual studio style project.
|
758
|
|
|
|
|
|
|
The first argument is the base name of the project, which will be used to
|
759
|
|
|
|
|
|
|
derive the F<.SLN> and other files.
|
760
|
|
|
|
|
|
|
It has the following parameters,
|
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=over 4
|
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=item solution=>name
|
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
The name of the solution file. This can be used to insert a .vcproj file
|
767
|
|
|
|
|
|
|
to have a similar effect.
|
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=item project=>name
|
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
The given project in the solution you wish to build.
|
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=item buildtype=>type
|
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
What sort of build you want to do. These are the support targets from
|
776
|
|
|
|
|
|
|
visual studio, such as /build (default), /rebuild, /clean, /deploy etc.
|
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=item log=>file
|
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
Where to output the log. The default is the base name with .log appended.
|
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=item target=>Release
|
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
The target build environment, usually Debug or Release.
|
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=item devenv=>devenv
|
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
The name of the devenv binary - which might be a full pathname.
|
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=back
|
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
=cut
|
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
sub BuildSolution {
|
795
|
0
|
|
|
0
|
1
|
0
|
my($sln, %args) = @_;
|
796
|
0
|
|
0
|
|
|
0
|
my $slnfile = $args{solution} || "$sln.sln";
|
797
|
0
|
|
0
|
|
|
0
|
my $project = $args{project} || $sln;
|
798
|
0
|
|
0
|
|
|
0
|
my $buildtype = $args{buildtype} || "/build";
|
799
|
0
|
|
0
|
|
|
0
|
my $log = $args{log} || "$sln.log";
|
800
|
0
|
|
0
|
|
|
0
|
my $buildtarget = $args{target} || "Release";
|
801
|
0
|
|
0
|
|
|
0
|
my $devenv = $args{devenv} || "devenv";
|
802
|
|
|
|
|
|
|
|
803
|
0
|
|
|
|
|
0
|
my $cmd = qq{$devenv $slnfile $buildtype "$buildtarget" /project $project /out $log};
|
804
|
0
|
|
|
|
|
0
|
return Command($cmd, log=>$log);
|
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
}
|
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
=head2 FindPatternInFile(file, pattern)
|
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
This function searches the given file line by line, until it finds the
|
811
|
|
|
|
|
|
|
pattern given, and returns the string matching the first bracketed
|
812
|
|
|
|
|
|
|
expression int the regexp. This can be used to
|
813
|
|
|
|
|
|
|
find things like file versions.
|
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
=over 4
|
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
my $ver = FindPatternInFile("thing.rc", qr/^\s*FILEVERSION\s*(\d+,\d+,\d+,\d+)/);
|
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
=back
|
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=cut
|
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
sub FindPatternInFile {
|
824
|
3
|
|
|
3
|
1
|
9
|
my ($file, $pat) = @_;
|
825
|
3
|
50
|
|
|
|
118
|
open(FILE, $file) || return undef;
|
826
|
3
|
|
|
|
|
44
|
while (my $line = ) {
|
827
|
10
|
100
|
|
|
|
60
|
if ($line =~ $pat) {
|
828
|
3
|
|
|
|
|
41
|
close(FILE);
|
829
|
3
|
|
|
|
|
38
|
return $1;
|
830
|
|
|
|
|
|
|
}
|
831
|
|
|
|
|
|
|
}
|
832
|
0
|
|
|
|
|
|
close(FILE);
|
833
|
|
|
|
|
|
|
}
|
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
1;
|
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
__END__
|