5 # This script generates a build tree with $ndirs + 1 directories, containing
6 # $nfils source files each, and both SConstruct files and non-recursive
7 # Makefiles to build the tree.
9 # Copyright (c) 2010 Electric Cloud, Inc.
10 # All rights reserved.
11 # Redistribution and use in source and binary forms, with or without
12 # modification, are permitted provided that the following conditions are met:
14 # * Redistributions of source code must retain the above copyright notice,
15 # this list of conditions and the following disclaimer.
16 # * Redistributions in binary form must reproduce the above copyright
17 # notice, this list of conditions and the following disclaimer in the
18 # documentation and/or other materials provided with the distribution.
19 # * Neither the name of Electric Cloud nor the names of its employees may
20 # be used to endorse or promote products derived from this software
21 # without specific prior written permission.
23 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 # POSSIBILITY OF SUCH DAMAGE.
35 # These settings will generate 2,000 total C files, grouped in blocks of 20,
36 # each of which does a couple of #includes.
38 $nlvls = 2; $ndirs = 3; $nfils = 500; $nlups = 2; $group = 20;
43 # Check Variables settings
45 #if ( ($nfils % $group) != 0) {
46 # die "ERROR: The number of files ($nfils) must be a multiple of the group size ($group)";
52 # use warnings FATAL => 'all';
54 my $opt_string = 'd:f:g:l:u:h';
55 getopts
( "$opt_string", \
%opt) or usage
();
59 $ndirs = $opt{d
} if $opt{d
};
60 $nfils = $opt{f
} if $opt{f
};
61 $group = $opt{g
} if $opt{g
};
62 $nlvls = $opt{l
} if $opt{l
};
63 $nlups = $opt{u
} if $opt{u
};
69 print STDERR
<< "EOF";
71 usage
: $0 [-l Levels
] [-d Dirs
] [-f Files
] [-g Grouping
] [-u Lookups
]
74 -l Levels
: number of levels of directories
(default $nlvls)
75 -d Dirs
: number of directories at
each level
(default $ndirs)
76 -f Files
: number of source files per directory
(default $nfils)
77 -g Grouping
: compile
in groups of Grouping files
(default $group)
78 -u Lookups
: number of lookups per source file
(default $nlups)
80 -h
: this help message
82 You can edit the
default values in genscons
.pl
90 # Adds commas to long numbers to make them more readable.
95 $value =~ s/([0-9])([0-9]{3})(?![0-9])/\1,\2/g;
96 $running = ($1 =~ /[0-9]/);
103 # Generate a generic include file to keep the compiler busy.
105 my ($basedir, $filename, $idx) = @_;
107 open (INC
, "> $filename") || die "Cannot open $filename for output.";
108 print INC
"#ifndef $filname[$idx]\n"
109 . "#define $filname[$idx] \"$basedir\"\n\n"
110 . "#include \"stdio.h\"\n\n";
111 print INC
"#endif\n";
117 # Generate a distinct C file to keep the compiler busy.
119 my ($basedir, $filename, $idx) = @_;
121 open (CFILE
, "> $basedir/$filename")
122 || die "Cannot open $basedir/$filename for output";
124 $buff = "#include <stdlib.h>\n";
125 $buff .= "#include <$filname[$idx].h>\n";
127 $buff .= "#include <omega.h>\n";
131 print CFILE
"int main (int argc, char * argv[]) {\n"
132 . "\tint i, mb_out;\n"
133 . "\tprintf (\"I am $basedir/%s\\n\", \"$filname[$idx]\""
135 . "\treturn (0);\n}\n";
137 elsif ( ($group - ($fil[$idx] % $group)) == 1) {
138 print CFILE
"int printr_$filname[$idx] (char * fname) {\n"
139 . " printf (\"I am $basedir/%s\\n\", fname);\n"
140 . " return (0);\n}\n";
142 elsif ( ($fil[$idx] % $group) == 0) {
143 $idx2 = $fil[$idx] + 1;
144 print CFILE
"extern int printr_$file[$idx2] (char * fname);\n"
145 . "int main (int argc, char * argv[]) {\n"
146 . "\tint i, mb_out;\n";
148 print CFILE
"\tprintr_$file[$idx2] (\"$filname[$idx]\");\n"
151 . "\tif (argc > 1) {\n"
152 . "\t\tmb_out = atoi (argv[1]);\n"
154 . "\tfor (i = 0; i < (mb_out * 16000); i++) {\n"
155 . "\t\tprintf (\"%07d 9a123456789b123456789c12345"
156 . "6789d123456789e123456789f12345678\\n\", i);\n"
158 . "\texit (0);\n}\n";
161 $idx2 = $fil[$idx] + 1;
162 print CFILE
"extern int printr_$file[$idx2] (char * fname);\n"
163 . "int printr_$filname[$idx] (char * fname) {\n"
164 . " printr_$file[$idx2] (fname);\n"
165 . " return (0);\n}\n";
172 # Recursive function for generating directories full of files to build, and
173 # the makefiles that go with them.
176 my ($idx, $basedir, $thisLvl) = @_;
178 if ( ! -d
$basedir ) {
179 mkdir $basedir || die "Cannot create folder $basedir: $!";
182 $relpath[$idx] = substr ($basedir, 8); # assumed top dir is "sconsbld"
183 if ("$relpath[$idx]" eq "") {
184 $relpath[$idx] = ".";
190 $basestr = $relpath[$idx];
192 $foo = substr($basedir,9) . "/";
193 $libdir = substr($basedir,9);
195 $bstr[$idx] = $basestr;
200 # $basedir is $relpath[$idx] with "sconsbld/" prepended so
201 # $dirstr is $basestr with "sconsbld_" prepended.
204 for ($cdidx = 1; $cdidx < $thisLvl; $cdidx++) { $cdstr .= "/.."; }
206 $thissc[$idx] = "$basedir/SConstruct";
207 $thismk[$idx] = "$basedir/Makefile";
208 $fzero[$idx] = "$basedir/file0";
210 open (SC
, "> $thissc[$idx]")
211 || die "Cannot open $thissc[$idx] for output: $!";
212 open (MK
, "> $thismk[$idx]")
213 || die "Cannot open $thismk[$idx] for output: $!";
215 print SC
"import os\n"
216 . "env = Environment(ENV = os.environ)\n\n";
219 for (my $junk = 1; $junk < $thisLvl; $junk++) { $cfgpath .= "../"; }
221 my $arkive = "archive";
223 if ($thisLvl == 1) { $mysubdir = "."; }
224 else { $mysubdir = substr ($basedir, 8); }
227 if (index ($basedir, "/") > 0) {
228 @pieces = split (/\//, $basedir);
230 for (my $ii =0; $ii <= $#pieces; $ii++) {
231 $fileSuffix .= "_" . $pieces[$ii];
234 $fileSuffix = "_" . $basedir;
237 for (my $lupsIdx = 0; $lupsIdx < $nlups; $lupsIdx++) {
238 printf SC
("env.Append (CPPPATH = ['./lup%03d$fileSuffix'])\n",
245 printf MK
("${foo}%%.o: CPPFLAGS $eq -I${foo}lup%03d$fileSuffix\n",
248 print SC
"env.Append (LIBPATH = ['.'])\n\n";
249 print MK
"${foo}%: LDFLAGS = -L$libdir\n\n";
253 . "env.Help(\"\"\"\n"
254 . "This build has parameters:\n"
255 . " number of levels = $nlvls\n"
256 . " directories/level = $ndirs\n"
257 . " cfiles/directory = $nfils\n"
258 . " lookups/source = $nlups\n"
259 . " compiles grouped = $group\n"
262 print MK
"${foo}%.a:\n";
263 print MK
"\tar rc \$@ \$^\n";
264 print MK
"\tranlib \$@\n\n";
265 print MK
"%.o: %.c\n";
266 print MK
"\t\$(CC) -MMD -o \$@ -c \$(CPPFLAGS) \$<\n\n";
268 print MK
"\t\$(CC) -o \$@ \$< \$(LDFLAGS) -l\$(notdir \$@)\n\n";
269 print MK
"CC=gcc\n\n";
270 print MK
"all:\n\t\@ps -eo vsz,rss,comm | fgrep make\n\n";
275 # Create include directories for doing additional lookups.
276 for (my $ii = 0; $ii < $nlups; $ii++) {
277 $lupDir = sprintf ("lup%03d$fileSuffix", $ii);
278 mkdir "$basedir/$lupDir" || die "Couldn't create $basedir/$lupDir: $!";
288 ### generate the .c files and the .h files they include.
289 ### Also generate the corresponding Makefile commands.
291 for (my $filidx = 0; $filidx < $nfils; $filidx++) {
292 $file[$filidx] = sprintf ("f%05d$fileSuffix", $filidx);
294 for ($fil[$idx] = 0; $fil[$idx] < $nfils; $fil[$idx]++) {
295 $filname[$idx] = "$file[$fil[$idx]]";
297 $nextnum = substr ($filname[$idx], 1, 5);
301 # Even when there are no groups, pre-compiled headers
304 print SC
"env.Program ('$filname[$idx].c')\n\n";
305 } # end of $group == 1
307 # Compile source files in groups.
308 # This removes unique lookups but adds some :: rules.
311 if ( ($fil[$idx] % $group) == 0) {
312 if ("$scfcc" ne "") {
313 print SC
"$scfcc\n$scfar\n\n";
317 if ("$mkfcc" ne "") {
318 print MK
"$mkfcc\n$mkfar\n\n";
323 $groupFilename = "$filname[$idx]";
324 $nextnum = substr ($filname[$idx], 1, 5);
326 $scfcc = "env.Program('$filname[$idx]',\n"
327 . "\tLIBS=['$filname[$idx]'])\n";
329 $scfar = "env.Library ('$filname[$idx]',\n"
330 . "\t['$filname[$idx].c'";
332 $mkfcc = "TARGETS += ${foo}$filname[$idx]\n${foo}$filname[$idx]: ${foo}$filname[$idx].o ${foo}lib$filname[$idx].a\n";
333 $mkfar = "${foo}lib$filname[$idx].a: ${foo}$filname[$idx].o";
335 print MK
"SRCS += ${foo}$filname[$idx].c\n";
336 $tmpfnam = $filname[$idx];
337 for ($filei = 1; $filei < $group; $filei++) {
338 $tmpfnum = sprintf ("%05d", $nextnum + $filei);
339 substr ($tmpfnam, 1, 5) = $tmpfnum;
341 $scfar .= ",\n\t '$tmpfnam.c'";
342 $mkfar .= " ${foo}$tmpfnam.o";
343 print MK
"SRCS += ${foo}$tmpfnam.c\n";
349 } # end of handling of compiles for $group > 1
351 gen_incfile
($basedir, "$basedir/$lupDir/$filname[$idx].h", $idx);
353 if ($fil[$idx] == 0) {
354 open (INC
, "> $basedir/$lupDir/omega.h")
355 || die "Cannot open $basedir/$lupDir/omega.h for output.";
356 print INC
"// comment in dummy file.\n";
360 gen_cfile
($basedir, "$filname[$idx].c", $idx);
361 } # end of generation of source files and header files
363 if ($group > 1 && "$scfcc" ne "") {
365 # create makefile commands for the leftover files
367 print SC
"$scfcc\n$scfar\n\n";
368 print MK
"$mkfcc\n$mkfar\n\n";
374 # Recurse and create more subdirectories and their contents.
375 if ($thisLvl < $nlvls) {
377 for ($dir[$idx] = 0; $dir[$idx] < $ndirs; $dir[$idx]++) {
378 $dirname[$idx] = sprintf ("d${thisLvl}_%d", $dir[$idx]);
379 $allsubs[$idx] .= "$dirname[$idx].subdir ";
381 # divide the subdirectories into 2 lists
382 # The two lists are/can be treated differently in Makefile.util
384 if ($dir[$idx] < ($ndirs / 2)) {
385 if ("$dirs1[$idx]" eq "") { $dirs1[$idx] = "$dirname[$idx]"; }
386 elsif (index ($dirs1[$idx], $dirname[$idx]) < 0) {
387 $dirs1[$idx] .= " $dirname[$idx]";
391 if ("$dirs2[$idx]" eq "") { $dirs2[$idx] = "$dirname[$idx]"; }
392 elsif (index ($dirs2[$idx], $dirname[$idx]) < 0) {
393 $dirs2[$idx] .= " $dirname[$idx]";
395 # The preceding elsif should really just be
396 # "else" but when nlvls > 2, you start getting repetition
397 # of directory names in $dirs1[$idx] and $dirs2[$idx].
398 # Rather than figure out where the duplication is coming
399 # from, just prevent it.
404 if ( ! -d
"$basedir/$dirname[$idx]") {
405 mkdir "$basedir/$dirname[$idx]" ||
406 die "Couldn't create $basedir/$dirname[$idx]: $!";
410 &mkdirs
($idx + 1, "$basedir/$dirname[$idx]", $thisLvl + 1);
412 print "Finished folder $dirname[$idx] in $basedir at "
419 if ($thisLvl < $nlvls) {
420 open (SC
, ">> $thissc[$idx]")
421 || die "Cannot open $thissc[$idx] for append: $!";
422 open (MK
, ">> $thismk[$idx]")
423 || die "Cannot open $thismk[$idx] for append: $!";
425 print SC
"SConscript([";
427 if (index ($dirs1[$idx], " ") > 0) {
428 @subdirs = split (/ /, $dirs1[$idx]);
429 for ($i = 0; $i <= $#subdirs; $i++) {
430 print SC
"'$subdirs[$i]/SConstruct',\n\t";
431 print MK
"include $subdirs[$i]/Makefile\n";
435 print SC
"'$dirs1[$idx]/SConstruct',\n\t";
436 print MK
"include $dirs1[$idx]/Makefile\n";
438 if (index ($dirs2[$idx], " ") > 0) {
439 @subdirs = split (/ /, $dirs2[$idx]);
440 for ($i = 0; $i < $#subdirs; $i++) {
441 print SC
"'$subdirs[$i]/SConstruct',\n\t";
442 print MK
"include $subdirs[$i]/Makefile\n";
444 print SC
"'$subdirs[$#subdirs]/SConstruct'";
445 print MK
"include $subdirs[$#subdirs]/Makefile\n";
448 print SC
"'$dirs2[$idx]/SConstruct'";
449 print MK
"include $dirs2[$idx]/Makefile\n";
454 print MK
"SPACE=\$(NUL) \$(NUL)\n";
455 print MK
"define nl\n\$(SPACE)\n\$(SPACE)\nendef\n\n";
456 print MK
"all: \$(TARGETS)\n\n";
458 print MK
"\t\$(foreach tgt,\$(TARGETS),rm -f \$(tgt)\$(nl))\n";
459 print MK
"\tfor n in d1*; do rm -f \$\$n/*.o ; rm -f \$\$n/*.a;done\n";
460 print MK
"\trm -f *.o ; rm -f *.a\n\n";
461 print MK
"-include \$(SRCS:.c=.d)";
469 $basedir = "sconsbld";
470 if ( ! -d
$basedir) { mkdir $basedir || die "Couldn't create $basedir: $!"; }
474 $numdirs = 0; # dirs other than include dirs
475 for ($i = 0; $i < $nlvls; $i++) { $numdirs += ($ndirs ** $i); }
477 $totldirs = $numdirs * ($nlups + 1); # dirs including include dirs
479 # total = ( .c ) + ( .h ) + mkfiles + Makefile.util
485 $totlfils = ($nfils * $numdirs) + ($nfils * $numdirs)
486 + $numdirs + 3 + $numdirs;
488 $totlobjs = $nfils * $numdirs;
489 $totlexes = $numdirs * ($nfils / $group);
491 $totllups = $nfils * $numdirs * $nlups / $group;
492 $allfiles = $totlfils + $totlobjs + $totlexes;
494 # one rule for each group plus overhead of 10/makefile
495 $nrules = ($numdirs * $nfils / $group) + ($numdirs * 10);
497 $txt1 = "Number of levels = $nlvls\n"
498 . "Number of dirs / level = $ndirs\n"
499 . "Number of source files / dir = $nfils\n"
500 . "Number of lookups / source file = $nlups\n"
501 . "Number of compiles grouped = $group\n";
507 $txt2 = "Expecting:\n"
508 . "\tdirectories: " . fmt
($totldirs) . "\n"
509 . "\tsource files: " . fmt
($numdirs * $nfils) . "\n"
510 . "\tinclude files: " . fmt
($numdirs * ($nfils + 1)) . "\n"
511 . "\tmakefiles: " . fmt
($numdirs * $numMakefiles)
512 . " ($numMakefiles per directory)\n"
513 . "\ttotal files: " . fmt
($totlfils) . "\n"
514 . "\tlook-ups: >= " . fmt
($totllups) . "\n"
515 . "\trules: >= " . fmt
($nrules) . "\n";
518 $txt3 = "When the build runs, " . fmt
($totlobjs) . " object files & "
519 . fmt
($totlexes) . " executable(s)"
520 . "\nwill be created, for a total of " . fmt
($allfiles) . " files.\n";
523 # Using local archives the number of conflicts is about the number of compiles
524 # which equals the number of archive writes.
528 print "Cleaning up from a previous run of this perl script.\n\n";
529 system ("rm -rf $basedir/*");
533 ### Generate README.txt
535 $readme = "$basedir/README.txt";
536 open (README
, "> $readme") || die "Cannot open $readme for output.";
538 $txt = "\nStarted at " . `date` . "\n";
544 . $txt3 || die "Cannot write txt, vartxt, txt1 etc to README";
547 ### Do the heavy lifting
550 print "Start writing new files at " . `date` . "......\n";
552 $basedir0 = $basedir;
553 &mkdirs
(0, $basedir, 1);
556 ### Summarize the results to the README and the console
559 $txt = "\nFile creation ended at " . `date` . "\n";
560 print $txt; print README
$txt || die "Cannot print txt to README";