1#!/usr/bin/perl
2
3use strict;
4use Data::Dumper;
5use Getopt::Std;
6use IO::File;
7our %opts = (d => 'size-tests');
8
9# default arguments go here
10my @sets =
11    ({
12	"with-defaults" => '',
13     });
14
15my %base_args = %{$sets[0]};
16my ($basename, $basenum);
17
18getopts('t:hlcD:nj:o:eb:T', \%opts) || usage();
19
20$opts{'j'} = "-j$opts{j}" if (exists($opts{'j'}));
21
22usage() if ($opts{'h'});
23
24my @argumentsets =
25    (
26     {
27	 title => 'security-types',
28	 arguments =>
29	 {
30	     usm => {
31		 'with-security-modules' => 'usm',
32	     },
33	     dtlsandtls => {
34		 'with-security-modules'     => 'tsm',
35		 'with-out-security-modules' => 'usm',
36		 'with-transports'           => 'DTLSUDP TLSTCP',
37		 'with-mib-modules'          => 'tlstm-mib tsm-mib'
38	     },
39	     usmanddtlsandtls => {
40		 'with-security-modules'     => 'tsm usm',
41		 'with-transports'           => 'DTLSUDP TLSTCP',
42		 'with-mib-modules'          => 'tlstm-mib tsm-mib'
43	     }
44	 }
45     },
46     {
47	 title => 'minimalist',
48	 arguments =>
49	 {
50	     minimal => {
51		 'enable-minimalist'    => '',
52	     },
53	     'not-minimal' => {
54	     }
55	 }
56     },
57     {
58	 title => 'mib-loading',
59	 arguments =>
60	 {
61	     'without-mib-loading' => {
62		 'disable-mib-loading'    => '',
63	     },
64	     'with-mib-loading' => {
65	     }
66	 }
67     },
68     {
69	 title => 'debugging',
70	 arguments =>
71	 {
72	     'without-debugging' => {
73		 'disable-debugging'    => '',
74	     },
75	     'with-debugging' => {
76	     }
77	 }
78     },
79     {
80	 title => 'logging',
81	 arguments =>
82	 {
83	     'without-logging' => {
84		 'with-out-features'    => 'logging',
85	     },
86	     'with-logging' => {
87	     }
88	 }
89     },
90     {
91	 title => 'agent-mibs',
92	 arguments =>
93	 {
94	     'full-agent' => {
95	     },
96	     'mini-agent' => {
97		 'enable-mini-agent' => '',
98	     }
99	 }
100     },
101     {
102	 title => 'protocol-support',
103	 arguments =>
104	 {
105	     'everything' => {
106	     },
107	     'read-only' => {
108		 'enable-read-only' => '',
109	     },
110	     'notify-only' => {
111		 'enable-notify-only' => '',
112	     }
113	 }
114     },
115     {
116	 title => 'perl',
117	 arguments =>
118	 {
119	     'without-perl-modules' => {
120		 'without-perl-modules'    => '',
121		 'disable-embedded-perl' => '',
122	     },
123	     'with-perl-no-embedding' => {
124		 'with-perl-modules'    => '',
125		 'disable-embedded-perl' => '',
126	     },
127	     'with-perl-and-embedding' => {
128		 'with-perl-modules'    => '',
129		 'enable-embedded-perl' => '',
130	     }
131	 }
132     },
133    );
134
135# map these to a referencable lookup hash
136my %argumentsets;
137foreach my $set (@argumentsets) {
138    $argumentsets{$set->{'title'}} = $set;
139}
140
141if ($opts{'l'}) {
142    print "Types available:\n";
143    printf("  %-40.40s %s\n", "Test Title", "Number of subtests");
144    printf("  %-40.40s %s\n", "-" x 39, "-" x length("Number of subtests"));
145    foreach my $type (@argumentsets) {
146	my @keys = keys(%{$type->{'arguments'}});
147	printf("  %-40.40s %s\n", $type->{'title'}, $#keys+1);
148    }
149    exit;
150}
151
152my %types;
153if ($opts{'t'}) {
154    my @types = split(/,\s*/, $opts{'t'});
155    foreach my $type (@types) {
156	$types{$type} = 1;
157    }
158}
159
160if ($opts{'b'}) {
161    # use this set as the base default set of arguments
162    ($basename, $basenum) = ($opts{'b'} =~ /(.*):(\d+)/);
163    if (!exists($argumentsets{$basename})) {
164	printf STDERR "failed to find set for -b argument: %s\n", $basename;
165	exit(1);
166    }
167    @sets = add_in_flags($argumentsets{$basename}, \%base_args, ());
168    @sets = @sets[$basenum];
169    %base_args = %{$sets[0]};
170}
171
172foreach my $set (@argumentsets) {
173    if (!$opts{'t'} || exists($types{$set->{'title'}})) {
174        if ($basename && $set->{'title'} eq $basename) {
175            next;
176        }
177	@sets = add_in_flags($set, \%base_args, @sets);
178    }
179}
180
181if ($opts{'c'}) {
182    # print the configure flags
183    foreach my $set (@sets) {
184	print "$set->{'title'}:\n";
185	print " ", generate_configure_flags($set), "\n";
186    }
187    exit;
188}
189
190my $summaryfile;
191if ($opts{'o'} && !$opts{'n'}) {
192    $summaryfile = new IO::File;
193    $summaryfile->open(">$opts{o}");
194}
195
196my $count = 0;
197foreach my $set (@sets) {
198    $count++;
199    build_set($count, $set);
200}
201
202sub add_in_flags {
203    my ($argumentset, $base_flags, @current_flags) = @_;
204
205    my @new_flags;
206
207    # do a linear search of the options
208    if (! $opts{'e'}) {
209	@new_flags = @current_flags;
210	foreach my $newargs (keys(%{$argumentset->{'arguments'}})) {
211	    my %flags = %$base_flags;
212
213	    $flags{'title'} = "$flags{'title'}:$newargs";
214
215	    foreach my $newflag (keys(%{$argumentset->{'arguments'}{$newargs}})) {
216
217		$flags{$newflag} .= " $argumentset->{'arguments'}{$newargs}{$newflag}";
218	    }
219	    push @new_flags, \%flags;
220	}
221	return @new_flags;
222    }
223
224    # or an exponential search
225    foreach my $flags (@current_flags) {
226	foreach my $newargs (keys(%{$argumentset->{'arguments'}})) {
227	    my %flags = %{$flags};  # copy the existing hash-set of flags
228
229	    if (exists($flags{'title'})) {
230		$flags{'title'} .= ", $newargs";
231	    } else {
232		$flags{'title'} .= "$newargs";
233	    }
234	    foreach my $newflag (keys(%{$argumentset->{'arguments'}{$newargs}})) {
235
236		$flags{$newflag} .= " $argumentset->{'arguments'}{$newargs}{$newflag}";
237	    }
238
239	    push @new_flags, \%flags;
240	}
241    }
242
243    return @new_flags;
244}
245
246sub generate_configure_flags {
247    my ($arguments) = @_;
248    my $line = "";
249    foreach my $arg (keys(%$arguments)) {
250	next if ($arg eq 'title');
251	if ($arguments->{$arg} =~ /^\s*$/) {
252	    $line .= " --$arg";
253	} else {
254	    $line .= " --$arg=\"$arguments->{$arg}\"";
255	}
256    }
257    return $line;
258}
259
260sub build_set {
261    my ($num, $arguments) = @_;
262
263    $num = sprintf("%03d", $num);
264
265    my $file;
266
267    if (!$opts{'n'}) {
268	mkdir $opts{'d'} if (! -d $opts{'d'});
269	die "failed to create the $opts{'d'} directory" if (! -d $opts{'d'});
270
271	$file = new IO::File;
272	$file->open(">$opts{'d'}/$num-0-cmd-summary");
273	$file->print("Creating output for: $arguments->{'title'}\n");
274    }
275
276    print "==================== $arguments->{'title'}\n";
277
278
279    System ($file, $num, "1-distclean", "make distclean");
280    System ($file, $num, "2-configure",
281	    "./configure " . generate_configure_flags($arguments));
282    System ($file, $num, "3-features", "make features");
283    System ($file, $num, "4-make", "make $opts{'j'}");
284    System ($file, $num, "5-unused-code", "perl local/minimalist/find-unused-code -g");
285    System ($file, $num, "6-testing", "make -C testing test") if ($opts{'T'});
286
287    if (!$opts{'n'}) {
288	analyze_size($arguments->{'title'}, "$opts{'d'}/$num-9-results");
289    }
290}
291
292sub analyze_size {
293    my ($title, $filename) = @_;
294
295    my $outfile = new IO::File;
296    $outfile->open(">$filename");
297
298    print "Results for: $title\n";
299    printf $outfile "Results for: $title\n";
300    printf ("%-50.50s %10s\n", "-" x 49, "-" x 10);
301    printf $outfile ("%-50.50s %10s\n", "-" x 49, "-" x 10);
302
303    my $totalsize = 0;
304    foreach my $buildfile (glob("snmplib/.libs/lib*.so.*.0.0"),
305			   glob("agent/.libs/lib*.so.*.0.0"),
306			   "agent/.libs/snmpd") {
307
308	my @info = stat($buildfile);
309	printf $outfile ("%-50.50s %10s\n", $buildfile, $info[7]);
310	printf("%-50.50s %10s\n", $buildfile, $info[7]);
311	$totalsize += $info[7];
312    }
313    printf $outfile ("%-50.50s %10s\n", "-" x 49, "-" x 10);
314    printf $outfile ("%-50.50s %10s bytes\n", "TOTAL", $totalsize);
315
316    printf("%-50.50s %10s\n", "-" x 49, "-" x 10);
317    printf("%-50.50s %10s bytes\n", "TOTAL", $totalsize);
318
319    if (defined($summaryfile)) {
320	my $restricted_title = $title;
321	$restricted_title =~ s/[^a-zA-Z]/_/g;
322	printf $summaryfile "%10s %s \n", $totalsize, $title;
323    }
324
325    return $totalsize;
326}
327
328sub usage {
329    print "Usage: $0 [FLAGS]\n\n";
330    print "FLAGS:\n";
331    print "  -h\t\thelp\n";
332    print "  -t TYPES\tSelect types to analyze (default = all)\n";
333    print "  -l\t\tList available types\n";
334    print "  -c\t\tPrint the configuration flags that would be used\n";
335    print "  -D DIR\tSave data results to this directory\n";
336    print "  -o FILE\tSave a complete summary chart to FILE\n";
337    print "  -n\t\tDry run -- only show the commands to execute\n";
338    print "  -j NUM\tExecute make with parallel building (-jNUM)\n";
339    print "  -e\t\tUse exponential expansion (all combos) of the requested options\n";
340    print "  -b NAME:NUM\tUse NAME and the NUM'th set as the base arguments to start from\n";
341    print "  -T\t\tRun 'make test' at each step as well\n";
342    exit;
343}
344
345sub System {
346    my $file  = shift;
347    my $num   = shift;
348    my $title = shift;
349    my $pipe = " 2>&1 | tee $opts{'d'}/$num-$title.out\n";
350
351    print "### ", join(" ", @_), $pipe;
352    if (!$opts{'n'} && $file) {
353	print $file "### ", join(" ", @_), "\n";
354    }
355
356    if (!$opts{'n'}) {
357	system(join(" ", @_) . $pipe);
358    }
359}
360