1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
{ See procedure "Usage". This code is in the public domain. }
Program GParMake;
Uses
Classes;
procedure Usage;
begin
writeln('GParMake: create make rules for parallel execution of testsuite');
writeln('Usage: gparmake [-a] <outputfile> <dirname> <startchunk> <tests_per_chunk> <test1> [<test2> ...]');
writeln('Output: makefile fragment with rules to run the tests in sequences of <tests_per_chunk>');
writeln;
halt(1);
end;
{ make all numbers of the same string length so they can be sorted
lexographically }
function rulenr2str(rulenr: longint): string;
var
i: longint;
begin
str(rulenr:9,rulenr2str);
for i:=1 to length(rulenr2str)-1 do
if rulenr2str[i]=' ' then
rulenr2str[i]:='0';
end;
procedure WriteChunkRule(rulenr: longint; const dirname, files: ansistring);
var
rulestr: string;
begin
rulestr:=rulenr2str(rulenr)+dirname;
writeln('$(TEST_OUTPUTDIR)/testchunk_',rulestr,'-stamp.$(TEST_FULL_TARGET): testprep-stamp.$(TEST_FULL_TARGET)');
writeln(#9'$(Q)$(DOTEST) $(DOTESTOPT) -Lchunk',rulestr,' -e ',files);
writeln(#9'$(ECHOREDIR) $(TEST_DATETIME) > $@');
writeln;
writeln('$(addsuffix .chunk',rulestr,', $(LOGFILES)) : $(TEST_OUTPUTDIR)/testchunk_',rulestr,'-stamp.$(TEST_FULL_TARGET)');
writeln;
writeln('.INTERMEDIATE: $(addsuffix .chunk',rulestr,', $(LOGFILES)) $(TEST_OUTPUTDIR)/testchunk_',rulestr,'-stamp.$(TEST_FULL_TARGET)');
writeln;
end;
var
startchunk: longint;
dirname : ansistring;
doappend: boolean;
FileList : TStringList;
Function ProcessArgs: longint;
var
i,
paramnr,
chunktargetsize,
chunksize,
chunknr,
nextfileindex,
error: longint;
testname,
nexttestname,
testlist,
s,
outputname: ansistring;
filelist : array of ansistring;
responsefile : text;
procedure AddFile(const s : ansistring);
begin
if nextfileindex>high(filelist) then
SetLength(filelist,length(filelist)+128);
filelist[nextfileindex]:=s;
inc(nextfileindex);
end;
procedure FlushChunk;
begin
WriteChunkRule(chunknr,dirname,testlist);
inc(chunknr);
testlist:='';
chunksize:=0;
end;
begin
if paramcount < 3 then
Usage;
doappend:=false;
paramnr:=1;
if paramstr(paramnr)='-a' then
begin
doappend:=true;
inc(paramnr);
end;
outputname:=paramstr(paramnr);
inc(paramnr);
dirname:=paramstr(paramnr);
inc(paramnr);
val(paramstr(paramnr),startchunk,error);
if error<>0 then
Usage;
inc(paramnr);
val(paramstr(paramnr),chunktargetsize,error);
if error<>0 then
Usage;
inc(paramnr);
{ only redirect output after all possible cases where we may have to write
the usage screen }
assign(output,outputname);
if doappend then
append(output)
else
rewrite(output);
chunknr:=startchunk;
chunksize:=0;
testlist:='';
nextfileindex:=0;
for i := paramnr to paramcount do
begin
if paramstr(i)[1]='@' then
begin
assign(responsefile,copy(paramstr(i),2,length(paramstr(i))));
reset(responsefile);
while not(eof(responsefile)) do
begin
readln(responsefile,s);
AddFile(s);
end;
close(responsefile);
end
else
AddFile(paramstr(i));
end;
for i := 0 to nextfileindex-1 do
begin
testname:=filelist[i];
testlist:=testlist+' '+testname;
inc(chunksize);
if chunksize>=chunktargetsize then
begin
if (i=nextfileindex-1) then
FlushChunk
else
begin
{ keep tests with the same name except for the last character in the same chunk,
because they may have to be executed in order (skip ".pp" suffix and last char) }
if i+1>=nextfileindex then
nexttestname:=''
else
nexttestname:=filelist[i+1];
if lowercase(copy(testname,1,length(testname)-4))<>lowercase(copy(nexttestname,1,length(nexttestname)-4)) then
FlushChunk;
end;
end;
end;
if chunksize<>0 then
FlushChunk;
ProcessArgs:=chunknr-1;
end;
procedure WriteWrapperRules(totalchunks: longint);
const
lognames: array[1..3] of string[11] = ('log','faillist','longlog');
var
logi,
i: longint;
begin
for logi:=1 to 3 do
begin
write('$(TEST_OUTPUTDIR)/',lognames[logi],' :');
for i:=startchunk to totalchunks do
write(' $(TEST_OUTPUTDIR)/',lognames[logi],'.chunk',rulenr2str(i)+dirname);
writeln;
{ if you have multiple rules for one (non-pattern) target, all
prerequisites will be merged, but only one of the rules can have a
recipe }
if not doappend then
begin
writeln(#9'$(Q)$(CONCAT) $(sort $^) $@');
writeln;
end;
writeln;
end;
if not doappend then
begin
writeln('gparmake_allexectests : $(LOGFILES)');
writeln;
end;
end;
var
totalchunks: longint;
begin
totalchunks:=ProcessArgs;
WriteWrapperRules(totalchunks);
close(output);
end.
|