📄 generateutils.pas
字号:
if CurLines.Count <> OutLines.Count then
begin
Result := True;
Exit;
end;
// Replace the time stamp line by the new one to ensure that this
// won't break the comparison.
if TimeStampLine > -1 then
CurLines[TimeStampLine] := OutLines[TimeStampLine];
Result := not CurLines.Equals(OutLines);
finally
CurLines.Free;
end;
end;
end;
{$IFNDEF COMPILER6_UP}
function FileSetDate(const Filename: string; FileAge:Integer):Integer;
var
Handle: Integer;
begin
Handle := FileOpen(Filename, fmOpenReadWrite);
try
Result := SysUtils.FileSetDate(Handle, FileAge);
finally
FileClose(Handle);
end;
end;
{$ENDIF !COMPILER6_UP}
procedure AdjustEndingSemicolon(Lines: TStrings);
var
S: string;
Len, Index: Integer;
begin
if Lines.Count > 0 then
begin
Index := Lines.Count - 1;
S := Lines[Index];
Len := Length(S);
{ If the last line is a comment then we have a problem. Here we allow the
last comment to have no comma }
if (Len > 2) and (S[1] = '{') and (S[2] = '$') and (Index > 0) then
begin
Dec(Index);
S := Lines[Index];
Len := Length(S);
end;
if Len > 0 then
begin
if S[Len] = ',' then
begin
Delete(S, Len, 1);
Lines[Index] := S;
end;
end;
end;
end;
function GetDescription(xml: TPackageXmlInfo; const target: string): string;
begin
if TargetList[GetNonPersoTarget(target)].IsCLX then
Result := xml.ClxDescription
else
Result := xml.Description;
end;
function ApplyTemplateAndSave(const path, target, package, extension
: string; template : TStrings; xml : TPackageXmlInfo;
const templateName, xmlName : string) : string;
var
OutFileName : string;
oneLetterType : string;
reqPackName : string;
incFileName : string;
outFile : TStringList;
curLine, curLineTrim : string;
tmpLines, repeatLines : TStrings;
I : Integer;
j : Integer;
tmpStr : string;
bcbId : string;
bcblibsList : TStrings;
TimeStampLine : Integer;
Count: Integer;
containsSomething : Boolean; // true if package will contain something
repeatSectionUsed : Boolean; // true if at least one repeat section was used
AddedLines: Integer;
IgnoreNextSemicolon: Boolean;
begin
outFile := TStringList.Create;
Result := '';
containsSomething := False;
repeatSectionUsed := False;
repeatLines := TStringList.Create;
tmpLines := TStringList.Create;
try
// read the xml file
OutFileName := xml.Name;
if xml.IsDesign then
begin
OutFileName := OutFileName + '-D';
oneLetterType := 'd';
end
else
begin
OutFileName := OutFileName + '-R';
oneLetterType := 'r';
end;
OutFileName := path + TargetToDir(target) + PathSeparator +
ExpandPackageName(OutFileName, target)+
Extension;
// The time stamp hasn't been found yet
TimeStampLine := -1;
// read the lines of the templates and do some replacements
i := 0;
Count := template.Count;
IgnoreNextSemicolon := False;
while i < Count do
begin
curLine := template[i];
if IsTrimmedStartsWith('<%%% ', curLine) then
begin
curLineTrim := Trim(curLine);
if curLine = '<%%% START REQUIRES %%%>' then
begin
Inc(i);
repeatSectionUsed := True;
repeatLines.Clear;
while (i < Count) and
not IsTrimmedString(template[i], '<%%% END REQUIRES %%%>') do
begin
repeatLines.Add(template[i]);
Inc(i);
end;
AddedLines := 0;
for j := 0 to xml.RequireCount - 1 do
begin
// if this required package is to be included for this target
if xml.Requires[j].IsIncluded(target) then
begin
tmpLines.Assign(repeatLines);
reqPackName := BuildPackageName(xml.Requires[j], target);
StrReplaceLines(tmpLines, '%NAME%', reqPackName);
// We do not say that the package contains something because
// a package is only interesting if it contains files for
// the given target
// containsSomething := True;
EnsureCondition(tmpLines, xml.Requires[j].Condition, target);
outFile.AddStrings(tmpLines);
Inc(AddedLines);
end;
end;
if (outFile.Count > 0) and (AddedLines = 0) then
begin
// delete "requires" clause.
j := outFile.Count - 1;
while (j > 0) and (Trim(outFile[j]) = '') do
Dec(j);
if CompareText(Trim(outFile[j]), 'requires') = 0 then
begin
outFile.Delete(j);
IgnoreNextSemicolon := True;
end;
end
else
// if the last character in the output file is
// a comma, then remove it. This possible comma will
// be followed by a carriage return so we look
// at the third character starting from the end
AdjustEndingSemicolon(outFile);
end
else if curLineTrim = '<%%% START FILES %%%>' then
begin
Inc(i);
repeatSectionUsed := True;
repeatLines.Clear;
while (i < Count) and
not IsTrimmedString(template[i], '<%%% END FILES %%%>') do
begin
repeatLines.Add(template[i]);
Inc(i);
end;
AddedLines := 0;
for j := 0 to xml.ContainCount - 1 do
begin
// if this included file is to be included for this target
if xml.Contains[j].IsIncluded(target) then
begin
tmpLines.Assign(repeatLines);
incFileName := xml.Contains[j].Name;
ApplyFormName(xml.Contains[j], tmpLines, target);
containsSomething := True;
EnsureCondition(tmpLines, xml.Contains[j].Condition, target);
outFile.AddStrings(tmpLines);
Inc(AddedLines);
// if this included file is not in the associated 'perso'
// target or only in the 'perso' target then return the
// 'perso' target name.
if IsNotInPerso(xml.Contains[j], target) or
IsOnlyInPerso(xml.Contains[j], target) then
Result := GetPersoTarget(target);
end;
end;
if (outFile.Count > 0) and (AddedLines = 0) then
begin
// delete "requires" clause.
j := outFile.Count - 1;
while (j > 0) and (Trim(outFile[j]) = '') do
Dec(j);
if CompareText(Trim(outFile[j]), 'contains') = 0 then
begin
outFile.Delete(j);
IgnoreNextSemicolon := True;
end;
end
else
// if the last character in the output file is
// a comma, then remove it. This possible comma will
// be followed by a carriage return so we look
// at the third character starting from the end
AdjustEndingSemicolon(outFile);
end
else if curLine = '<%%% START FORMS %%%>' then
begin
Inc(i);
repeatSectionUsed := True;
repeatLines.Clear;
while (i < Count) and
not IsTrimmedString(template[i], '<%%% END FORMS %%%>') do
begin
repeatLines.Add(template[i]);
Inc(i);
end;
for j := 0 to xml.ContainCount - 1 do
begin
// if this included file is to be included for this target
// and there is a form associated to the file
if xml.Contains[j].IsIncluded(target) then
begin
containsSomething := True;
if (xml.Contains[j].FormName <> '') then
begin
tmpLines.Assign(repeatLines);
ApplyFormName(xml.Contains[j], tmpLines, target);
EnsureCondition(tmpLines, xml.Contains[j].Condition, target);
outFile.AddStrings(tmpLines);
end;
// if this included file is not in the associated 'perso'
// target or only in the 'perso' target then return the
// 'perso' target name.
if IsNotInPerso(xml.Contains[j], target) or
IsOnlyInPerso(xml.Contains[j], target) then
Result := GetPersoTarget(target);
end;
end;
end
else if curLine = '<%%% START LIBS %%%>' then
begin
Inc(i);
repeatLines.Clear;
while (i < Count) and
not IsTrimmedString(template[i], '<%%% END LIBS %%%>') do
begin
repeatLines.Add(template[i]);
Inc(i);
end;
// read libs as a string of comma separated value
bcbId := TargetList[GetNonPersoTarget(target)].Env+TargetList[GetNonPersoTarget(target)].Ver;
bcblibsList := nil;
if CompareText(bcbId, 'c6') = 0 then
bcblibsList := xml.C6Libs
else
if CompareText(bcbId, 'c5') = 0 then
bcblibsList := xml.C5Libs;
if bcblibsList <> nil then
begin
for j := 0 to bcbLibsList.Count - 1 do
begin
tmpLines.Assign(repeatLines);
MacroReplaceLines(tmpLines, '%',
['FILENAME%', bcblibsList[j],
'UNITNAME%', GetUnitName(bcblibsList[j])]);
outFile.AddStrings(tmpLines);
end;
end;
end
end
else
begin
if Pos('%', curLine) > 0 then
begin
tmpStr := curLine;
if MacroReplace(curLine, '%',
['NAME%', PathExtractFileNameNoExt(OutFileName),
'XMLNAME%', ExtractFileName(xmlName),
'DESCRIPTION%', GetDescription(xml, target),
'C5PFLAGS%', EnsurePFlagsCondition(xml.C5PFlags, target),
'C6PFLAGS%', EnsurePFlagsCondition(xml.C6PFlags, target),
'GUID%', xml.GUID,
'TYPE%', Iff(xml.IsDesign, 'DESIGN', 'RUN'),
'DATETIME%', FormatDateTime('dd-mm-yyyy hh:nn:ss', NowUTC) + ' UTC',
'type%', OneLetterType]) then
begin
if Pos('%DATETIME%', tmpStr) > 0 then
TimeStampLine := I;
end;
end;
if IgnoreNextSemicolon then
begin
if (Trim(curLine) <> '') and (Trim(curLine) = ';') then
IgnoreNextSemicolon := False
else
outFile.Add(curLine);
end
else
outFile.Add(curLine);
end;
Inc(i);
end;
// test if there are required packages and/or contained files
// that make the package require a different version for a
// perso target. This is determined like that:
// if a file is not in the associated 'perso'
// target or only in the 'perso' target then return the
// 'perso' target name.
for j := 0 to xml.RequireCount - 1 do
begin
if IsNotInPerso(xml.Requires[j], target) or
IsOnlyInPerso(xml.Requires[j], target) then
Result := GetPersoTarget(target);
end;
for j := 0 to xml.ContainCount - 1 do
begin
if IsNotInPerso(xml.Contains[j], target) or
IsOnlyInPerso(xml.Contains[j], target) then
Result := GetPersoTarget(target);
end;
// if no repeat section was used, we must check manually
// that at least one file is to be used by the given target.
// This will then force the generation of the output file
// (Useful for cfg templates for instance).
// We do not check for the use of "required" packages because
// a package is only interesting if it contains files for
// the given target
if not repeatSectionUsed then
begin
for j := 0 to xml.ContainCount - 1 do
if xml.Contains[j].IsIncluded(target) then
begin
containsSomething := True;
Break;
end;
end;
// Save the file, if it contains something, and it
// has changed when compared with the existing one
if containsSomething and
(HasFileChanged(OutFileName, templateName, outFile, TimeStampLine)) then
begin
tmpStr := ExtractFilePath(templateName);
if tmpStr[length(tmpStr)] = PathSeparator then
SetLength(tmpStr, length(tmpStr)-1);
if ExtractFileName(tmpStr) = TargetList[GetNonPersoTarget(target)].PDir then
SendMsg(SysUtils.Format(#9#9'Writing %s for %s (%s template used)', [ExtractFileName(OutFileName), target, target]))
else
SendMsg(SysUtils.Format(#9#9'Writing %s for %s', [ExtractFileName(OutFileName), target]));
// if outfile contains line, save it.
// else, it's because the template file was a binary file, so simply
// copy it to the destination name
SetFileAttributes(PChar(OutFileName), 0); // do not fail on read only files
if outFile.count > 0 then
outFile.SaveToFile(OutFileName)
else
begin
CopyFile(PChar(templateName), PChar(OutFileName), False);
FileSetDate(OutFileName, DateTimeToFileDate(Now)); // adjust file time
end;
end;
finally
tmpLines.Free;
repeatLines.Free;
outFile.Free;
end;
end;
function Max(d1, d2 : TDateTime): TDateTime;
begin
if d1 > d2 then
Result := d1
else
Result := d2;
end;
function IsBinaryFile(const Filename: string): Boolean;
const
BufferSize = 50;
BinaryPercent = 10;
var
F : TFileStream;
Buffer : array[0..BufferSize] of Char;
I, Index : Integer;
BinaryCount : Integer;
begin
Result := False;
// If the cache contains information on that file, get the result
// from it and skip the real test
if IsBinaryCache.Find(FileName, Index) then
begin
Result := Boolean(IsBinaryCache.Objects[Index]);
Exit;
end;
// Read the first characters of the file and if enough of them
// are not text characters, then consider the file to be binary
if FileExists(FileName) then
begin
F := TFileStream.Create(FileName, fmOpenRead);
try
F.Read(Buffer, BufferSize+1);
BinaryCount := 0;
for I := 0 to BufferSize do
if not (Buffer[I] in [#9, #13, #10, #32..#127]) then
Inc(BinaryCount);
Result := BinaryCount > BufferSize * BinaryPercent div 100;
finally
F.Free;
end;
end;
// save the result in the cache
IsBinaryCache.AddObject(FileName, TObject(Result));
end;
// loads the .inc file into Defines and returns True if the Filename contains
// a "%t"
function LoadDefines(const Target: string; Filename: string): Boolean;
var
incfile : TStringList;
ps: Integer;
begin
Result := False;
FreeAndNil(DefinesList);
// read the include file for this target or the default file if jvclxx.inc does not exist
incfile := TStringList.Create;
try
ps := Pos('%t', Filename);
if ps > 0 then
begin
Delete(Filename, ps, 2);
Insert(LowerCase(Target), Filename, ps);
if not FileExists(Filename) then
Filename := GIncDefFileName;
Result := True;
end;
if FileExists(Filename) then
incfile.LoadFromFile(Filename);
DefinesList := TDefinesList.Create(incfile);
finally
incfile.free;
end;
end;
function Generate(packages : TStrings;
targets : TStrings;
callback : TGenerateCallback;
const XmlFileName : string;
const ModelName : string;
var ErrMsg : string;
path : string = '';
prefix : string = '';
format : string = '';
incfileName : string = ''
) : Boolean;
var
rec : TSearchRec;
i : Integer;
j : Integer;
templateName, templateNamePers : string;
xml : TPackageXmlInfo;
xmlName : string;
template, templatePers : TStringList;
persoTarget : string;
target : string;
GenericIncFile: Boolean;
begin
Result := True;
if packages.Count = 0 then
begin
ErrMsg := '[Error] No package to generate, no xml file found';
Result := False;
Exit;
end;
if not LoadConfig(XmlFileName, ModelName, ErrMsg) then
begin
Result := False;
Exit;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -