IDE: support the new fpc -h and -i syntax when parsing all available options.

git-svn-id: trunk@47177 -
This commit is contained in:
juha 2014-12-12 00:49:13 +00:00
parent a949da703b
commit d59f67b95c

View File

@ -93,7 +93,7 @@ type
fVisible: Boolean; // Used for filtering. fVisible: Boolean; // Used for filtering.
fIgnored: Boolean; // Pretend this option does not exist. fIgnored: Boolean; // Pretend this option does not exist.
fChoices: TStrings; // Choices got from "fpc -i" fChoices: TStrings; // Choices got from "fpc -i"
procedure AddChoices(aCategory: string); procedure AddChoicesByOptOld;
function Comment: string; function Comment: string;
procedure Filter(aFilter: string; aOnlySelected: Boolean); procedure Filter(aFilter: string; aOnlySelected: Boolean);
function GenerateOptValue(aUseComments: Boolean): string; function GenerateOptValue(aUseComments: Boolean): string;
@ -172,20 +172,26 @@ type
fDefines: TStringList; fDefines: TStringList;
// Options not accepted by parser. They may still be valid (a macro maybe) // Options not accepted by parser. They may still be valid (a macro maybe)
fInvalidOptions: TStringList; // and will be included in output. fInvalidOptions: TStringList; // and will be included in output.
// Lists of selections parsed from "fpc -i". Contains supported technologies. // List of categories parsed from "fpc -i". Contains category names,
// Objects[] contains another StringList for the selection list.
fSupportedCategories: TStringList; fSupportedCategories: TStringList;
// Hierarchy of options parsed from "fpc -h". // Hierarchy of options parsed from "fpc -h".
fRootOptGroup: TCompilerOptGroup; fRootOptGroup: TCompilerOptGroup;
fCompilerExecutable: string; // Compiler path must be set by caller. fCompilerExecutable: string; // Compiler path must be set by caller.
//fCompilerVersion: string; // Parsed from "fpc -h". fFpcVersion: string; // Parsed from "fpc -h".
fIsNewFpc: Boolean;
fParsedTarget: String; fParsedTarget: String;
fErrorMsg: String; fErrorMsg: String;
fGenStrings: TStringList; // Options generated from GUI. fGenStrings: TStringList; // Options generated from GUI.
fUseComments: Boolean; // Add option's description into generated data. fUseComments: Boolean; // Add option's description into generated data.
function AddChoicesNew(aOpt: string): TStrings;
function AddNewCategory(aCategoryName: String): TStringList;
function AddOptInLowestOrigLine(OutStrings: TStrings): Boolean; function AddOptInLowestOrigLine(OutStrings: TStrings): Boolean;
procedure CopyOptions(aRoot: TCompilerOpt); procedure CopyOptions(aRoot: TCompilerOpt);
function FindLowestOrigLine(aStrings: TStrings; out aOrigLine: Integer): integer; function FindLowestOrigLine(aStrings: TStrings; out aOrigLine: Integer): integer;
//procedure ReadVersion(s: string); function IsGroup(aOpt: string; var aCategoryList: TStrings): Boolean;
function ReadCategorySelections(aChar: Char): TStringList;
function ReadVersion(s: string): Boolean;
procedure CreateNewGroupItem(aGroup: TCompilerOptGroup; aTxt: string); procedure CreateNewGroupItem(aGroup: TCompilerOptGroup; aTxt: string);
procedure AddGroupItems(aGroup: TCompilerOptGroup; aItems: TStrings); procedure AddGroupItems(aGroup: TCompilerOptGroup; aItems: TStrings);
function ParseI(aLines: TStringList): TModalResult; function ParseI(aLines: TStringList): TModalResult;
@ -196,15 +202,13 @@ type
procedure Clear; procedure Clear;
function UpdateTargetParam: Boolean; function UpdateTargetParam: Boolean;
function ReadAndParseOptions: TModalResult; function ReadAndParseOptions: TModalResult;
function ParseOptions(OutputI, OutputH: TStringList): TModalResult;
procedure ReadCompiler(CompPath, Params: string; out OutputI, OutputH: TStringList);
function FilterOptions(aFilter: string; aOnlySelected: Boolean): Boolean; function FilterOptions(aFilter: string; aOnlySelected: Boolean): Boolean;
function FindOptionById(aId: integer): TCompilerOpt; function FindOptionById(aId: integer): TCompilerOpt;
function FromCustomOptions(aStrings: TStrings): TModalResult; function FromCustomOptions(aStrings: TStrings): TModalResult;
function ToCustomOptions(aStrings: TStrings; aUseComments: Boolean): TModalResult; function ToCustomOptions(aStrings: TStrings; aUseComments: Boolean): TModalResult;
public public
property Defines: TStringList read fDefines; property Defines: TStringList read fDefines;
property SupportedCategories: TStringList read fSupportedCategories; //property SupportedCategories: TStringList read fSupportedCategories;
property RootOptGroup: TCompilerOptGroup read fRootOptGroup; property RootOptGroup: TCompilerOptGroup read fRootOptGroup;
property CompilerExecutable: string read fCompilerExecutable write fCompilerExecutable; property CompilerExecutable: string read fCompilerExecutable write fCompilerExecutable;
property ParsedTarget: String read fParsedTarget write fParsedTarget; property ParsedTarget: String read fParsedTarget write fParsedTarget;
@ -217,10 +221,6 @@ type
private private
fReader: TCompilerOptReader; fReader: TCompilerOptReader;
fReadTime: TDateTime; fReadTime: TDateTime;
fCompPath: string;
fCompParams: string;
fOutputI: TStringList;
fOutputH: TStringList;
fStartedOnce: boolean; fStartedOnce: boolean;
function GetErrorMsg: string; function GetErrorMsg: string;
procedure Clear; // (main thread) procedure Clear; // (main thread)
@ -239,9 +239,6 @@ type
implementation implementation
var
CurrentCategories: TStringList; // To pass categories to options parser.
{ TCompiler } { TCompiler }
{------------------------------------------------------------------------------ {------------------------------------------------------------------------------
@ -367,6 +364,7 @@ end;
var var
OptionIdCounter: integer; OptionIdCounter: integer;
function NextOptionId: integer; function NextOptionId: integer;
begin begin
Result := OptionIdCounter; Result := OptionIdCounter;
@ -393,25 +391,6 @@ begin
Result := aOpt[2] in ['i', 'F', 'e', 'o', 'd', 'u', 'M', 'T']; Result := aOpt[2] in ['i', 'F', 'e', 'o', 'd', 'u', 'M', 'T'];
end; end;
function IsGroup(aOpt: string; var aCategoryList: TStrings): Boolean;
// This option should be a group instead of a selection list.
var
i: Integer;
Category: string;
begin
if AnsiStartsStr('-Oo', aOpt) then
Category := 'Optimizations:'
else if AnsiStartsStr('-OW', aOpt) or AnsiStartsStr('-Ow', aOpt) then
Category := 'Whole Program Optimizations:'
;
Result := Category <> '';
if Result then
if CurrentCategories.Find(Category, i) then
aCategoryList := CurrentCategories.Objects[i] as TStrings
else
raise Exception.CreateFmt('No list of options found for "%s".', [Category]);
end;
{ TCompilerOpt } { TCompilerOpt }
@ -430,15 +409,35 @@ begin
inherited Destroy; inherited Destroy;
end; end;
procedure TCompilerOpt.AddChoices(aCategory: string); procedure TCompilerOpt.AddChoicesByOptOld;
// Add selection choices for this option. Data originates from "fpc -i". // From FPC 2.6.x output
var
i: Integer; procedure AddChoices(aCategory: string);
// Add selection choices for this option. Data originates from "fpc -i".
var
i: Integer;
begin
with fOwnerGroup.fOwnerReader do
if fSupportedCategories.Find(aCategory, i) then
fChoices := fSupportedCategories.Objects[i] as TStrings
else
raise Exception.CreateFmt('No selection list for "%s" found.', [aCategory]);
end;
begin begin
if CurrentCategories.Find(aCategory, i) then if Pos('fpc -i', fDescription) = 0 then Exit;
fChoices := CurrentCategories.Objects[i] as TStrings fEditKind := oeList; // Values will be got later.
else case fOption of
raise Exception.CreateFmt('No selection list for "%s" found.', [aCategory]); '-Ca': AddChoices('ABI targets:');
'-Cf': AddChoices('FPU instruction sets:');
'-Cp': AddChoices('CPU instruction sets:');
// '-Oo', '-Oo[NO]': AddChoices('Optimizations:');
'-Op': AddChoices('CPU instruction sets:');
// '-OW': AddChoices('Whole Program Optimizations:');
// '-Ow': AddChoices('Whole Program Optimizations:');
else
raise Exception.Create('Don''t know where to get selection list for option '+fOption);
end;
end; end;
procedure TCompilerOpt.ParseEditKind; procedure TCompilerOpt.ParseEditKind;
@ -450,21 +449,13 @@ begin
'x': fEditKind:=oeText; // <x> 'x': fEditKind:=oeText; // <x>
'n': fEditKind:=oeNumber; // <n> 'n': fEditKind:=oeNumber; // <n>
end; end;
if Pos('fpc -i', fDescription) > 0 then if fOwnerGroup.fOwnerReader.fIsNewFpc then begin
begin fChoices := fOwnerGroup.fOwnerReader.AddChoicesNew(fDescription);
fEditKind := oeList; // Values will be got later. if Assigned(fChoices) then
case fOption of fEditKind := oeList;
'-Ca': AddChoices('ABI targets:'); end
'-Cf': AddChoices('FPU instruction sets:'); else
'-Cp': AddChoices('CPU instruction sets:'); AddChoicesByOptOld;
// '-Oo', '-Oo[NO]': AddChoices('Optimizations:');
'-Op': AddChoices('CPU instruction sets:');
// '-OW': AddChoices('Whole Program Optimizations:');
// '-Ow': AddChoices('Whole Program Optimizations:');
else
raise Exception.Create('Don''t know where to get selection list for option '+fOption);
end;
end;
end; end;
procedure TCompilerOpt.ParseOption(aDescr: string; aIndent: integer); procedure TCompilerOpt.ParseOption(aDescr: string; aIndent: integer);
@ -824,6 +815,7 @@ procedure TCompilerOptSet.SelectOptions(aOptStr: string);
var var
i, Start: Integer; i, Start: Integer;
OneOpt: string; OneOpt: string;
OptOk: Boolean;
begin begin
i := 1; i := 1;
while i <= Length(aOptStr) do while i <= Length(aOptStr) do
@ -836,14 +828,11 @@ begin
Inc(i); Inc(i);
OneOpt := Copy(aOptStr, Start, i-Start); OneOpt := Copy(aOptStr, Start, i-Start);
if OneOpt[1] in ['0'..'9'] then if OneOpt[1] in ['0'..'9'] then
begin OptOk := SetNumberOpt(OneOpt)
if not SetNumberOpt(OneOpt) then else
raise Exception.CreateFmt('Numeric value is not allowed for set %s.', [fOption]); OptOk := False;
end if not (OptOk or SetBooleanOpt(OneOpt)) then
else begin raise Exception.CreateFmt('Option %s is not found in set %s.', [OneOpt, fOption]);
if not SetBooleanOpt(OneOpt) then
raise Exception.CreateFmt('Option %s is not found in set %s.', [OneOpt, fOption]);
end;
end; end;
end; end;
@ -918,8 +907,6 @@ begin
fSupportedCategories := TStringList.Create; fSupportedCategories := TStringList.Create;
fGenStrings := TStringList.Create; fGenStrings := TStringList.Create;
fRootOptGroup := TCompilerOptGroup.Create(Self, Nil); fRootOptGroup := TCompilerOptGroup.Create(Self, Nil);
// Categories are passed to options parser through a global variable.
CurrentCategories := fSupportedCategories;
end; end;
destructor TCompilerOptReader.Destroy; destructor TCompilerOptReader.Destroy;
@ -927,7 +914,6 @@ begin
Clear; Clear;
fRootOptGroup.Free; fRootOptGroup.Free;
fGenStrings.Free; fGenStrings.Free;
CurrentCategories:=nil;
fSupportedCategories.Free; fSupportedCategories.Free;
fInvalidOptions.Free; fInvalidOptions.Free;
fDefines.Free; fDefines.Free;
@ -944,12 +930,76 @@ begin
fSupportedCategories.Clear; fSupportedCategories.Clear;
end; end;
function TCompilerOptReader.AddChoicesNew(aOpt: string): TStrings;
// From FPC 2.7.1+ output
const
FpcIStart = 'see fpc -i or fpc -i';
var
ch: Char;
i: SizeInt;
begin
Result := Nil;
i := Pos(FpcIStart, aOpt);
if i = 0 then Exit;
Assert(Length(aOpt) >= i+Length(FpcIStart));
ch := aOpt[i+Length(FpcIStart)]; // Pick the next char from description.
if fSupportedCategories.Find(ch, i) then
Result := fSupportedCategories.Objects[i] as TStrings
else begin
Result := ReadCategorySelections(ch);
Result.Insert(0, ''); // First an empty string. Allows removing selection.
fSupportedCategories.AddObject(ch, Result);
end;
end;
function TCompilerOptReader.IsGroup(aOpt: string; var aCategoryList: TStrings): Boolean;
// This option should be a group instead of a selection list.
// The information is not available in fpc -h output.
var
i: Integer;
CategoryName: string;
begin
Result := False;
if fIsNewFpc then
begin
// FPC 2.7.1+
if AnsiStartsStr('-Oo', aOpt)
or AnsiStartsStr('-OW', aOpt)
or AnsiStartsStr('-Ow', aOpt) then
begin
aCategoryList := AddChoicesNew(aOpt);
Result := Assigned(aCategoryList);
end;
end
else begin
// FPC 2.6.x
CategoryName := '';
if AnsiStartsStr('-Oo', aOpt) then
CategoryName := 'Optimizations:'
else if AnsiStartsStr('-OW', aOpt) or AnsiStartsStr('-Ow', aOpt) then
CategoryName := 'Whole Program Optimizations:';
Result := CategoryName <> '';
if Result then
if fSupportedCategories.Find(CategoryName, i) then
aCategoryList := fSupportedCategories.Objects[i] as TStrings
else
raise Exception.CreateFmt('No list of options found for "%s".', [CategoryName]);
end;
end;
function TCompilerOptReader.AddNewCategory(aCategoryName: String): TStringList;
begin
Result := TStringList.Create;
Result.Add(''); // First an empty string. Allows removing selection.
fSupportedCategories.AddObject(aCategoryName, Result);
end;
function TCompilerOptReader.ParseI(aLines: TStringList): TModalResult; function TCompilerOptReader.ParseI(aLines: TStringList): TModalResult;
const const
Supported = 'Supported '; Supported = 'Supported ';
var var
i, j: Integer; i, j: Integer;
s, Line, TrimmedLine: String; Line, TrimmedLine: String;
Category, sl: TStringList; Category, sl: TStringList;
begin begin
Result := mrOK; Result := mrOK;
@ -970,41 +1020,58 @@ begin
if Line[1] <> ' ' then if Line[1] <> ' ' then
raise Exception.Create('TCompilerReader.ParseI: Line should start with a space.'); raise Exception.Create('TCompilerReader.ParseI: Line should start with a space.');
sl.Clear; sl.Clear;
// Some old FPC versions had a comma separated list.
sl.DelimitedText := Trim(Line); sl.DelimitedText := Trim(Line);
for j := 0 to sl.Count-1 do for j := 0 to sl.Count-1 do
Category.Add(sl[j]); Category.Add(sl[j]);
end; end;
end end
else if AnsiStartsStr(Supported, Line) then else if AnsiStartsStr(Supported, Line) then
begin Category := AddNewCategory(Copy(Line, Length(Supported)+1, Length(Line)));
Category := TStringList.Create;
Category.Add(''); // First an empty string. Allows removing selection.
s := Copy(Line, Length(Supported)+1, Length(Line));
fSupportedCategories.AddObject(s, Category);
end;
end; end;
fSupportedCategories.Sorted := True; fSupportedCategories.Sorted := True;
finally finally
sl.Free; sl.Free;
end; end;
end; end;
{
procedure TCompilerOptReader.ReadVersion(s: string); function TCompilerOptReader.ReadVersion(s: string): Boolean;
const const
VersBegin = 'Free Pascal Compiler version '; VersBegin = 'Free Pascal Compiler version ';
var var
i, Start: Integer; Start, V1, V2: Integer;
OutputI: TStringList; // fpc -Fr$(FPCMsgFile) -i
begin begin
if AnsiStartsStr(VersBegin, s) then Result := AnsiStartsStr(VersBegin, s);
if Result then
begin begin
Start := Length(VersBegin); fIsNewFpc := False;
i := PosEx(' ', s, Start+1); Start := Length(VersBegin)+1;
if i > 0 then V1 := PosEx(' ', s, Start);
fCompilerVersion := Copy(s, Start, i-Start); if V1 > 0 then
// ToDo: the rest 2 fields are date and target CPU. begin
fFpcVersion := Copy(s, Start, V1-Start);
if (Length(fFpcVersion)>2) then begin
V1 := StrToIntDef(fFpcVersion[1], 0);
V2 := StrToIntDef(fFpcVersion[3], 0);
fIsNewFpc := ((V1=2) and (V2>=7)) or (V1>2);
end;
// The rest 2 fields are date and target CPU.
end;
if not fIsNewFpc then
begin
// Get categories with FPC -i, once we know the version is old (2.6.x).
OutputI := RunTool(fCompilerExecutable, fParsedTarget + ' -i');
if OutputI = Nil then Exit(False);
try
Result := ParseI(OutputI) = mrOK;
finally
OutputI.Free;
end;
end;
end; end;
end; end;
}
procedure TCompilerOptReader.CreateNewGroupItem(aGroup: TCompilerOptGroup; aTxt: string); procedure TCompilerOptReader.CreateNewGroupItem(aGroup: TCompilerOptGroup; aTxt: string);
var var
Opt: TCompilerOpt; Opt: TCompilerOpt;
@ -1045,12 +1112,13 @@ begin
ThisLine := StringReplace(aLines[i],'-Agas-darwinAssemble','-Agas-darwin Assemble',[]); ThisLine := StringReplace(aLines[i],'-Agas-darwinAssemble','-Agas-darwin Assemble',[]);
ThisInd := CalcIndentation(ThisLine); ThisInd := CalcIndentation(ThisLine);
ThisLine := Trim(ThisLine); ThisLine := Trim(ThisLine);
if ThisInd < 2 then Continue; //Call if needed: ReadVersion(ThisLine);// Top header lines for compiler version etc. // Top header line for compiler version, check only once.
if (fFpcVersion = '') and ReadVersion(ThisLine) then Continue;
if ThisInd < 2 then Continue;
if (ThisLine = '') or (ThisInd > 30) if (ThisLine = '') or (ThisInd > 30)
or (ThisLine[1] = '@') or (ThisLine[1] = '@')
or (Pos('-? ', ThisLine) > 0) or (Pos('-? ', ThisLine) > 0)
or (Pos('-h ', ThisLine) > 0) then Continue; or (Pos('-h ', ThisLine) > 0) then Continue;
if i < aLines.Count-1 then begin if i < aLines.Count-1 then begin
NextLine := aLines[i+1]; NextLine := aLines[i+1];
NextInd := CalcIndentation(aLines[i+1]); NextInd := CalcIndentation(aLines[i+1]);
@ -1104,53 +1172,36 @@ var
begin begin
NewTarget := '-T$(TargetOS) -P$(TargetCPU)'; NewTarget := '-T$(TargetOS) -P$(TargetCPU)';
if not GlobalMacroList.SubstituteStr(NewTarget) then if not GlobalMacroList.SubstituteStr(NewTarget) then
raise Exception.CreateFmt('ReadAndParseOptions: Cannot substitute macros "%s".', raise Exception.CreateFmt('UpdateTargetParam: Cannot substitute macros "%s".',
[NewTarget]); [NewTarget]);
Result := fParsedTarget <> NewTarget; Result := fParsedTarget <> NewTarget;
if Result then if Result then
fParsedTarget := NewTarget; // fParsedTarget is used as a param for FPC. fParsedTarget := NewTarget; // fParsedTarget is used as a param for FPC.
end; end;
function TCompilerOptReader.ReadCategorySelections(aChar: Char): TStringList;
// Get the selection list for a category using "fpc -i+char", for new FPC versions.
begin
Result:=RunTool(fCompilerExecutable, fParsedTarget + ' -i' + aChar);
end;
function TCompilerOptReader.ReadAndParseOptions: TModalResult; function TCompilerOptReader.ReadAndParseOptions: TModalResult;
// fpc -Fr$(FPCMsgFile) -h // fpc -Fr$(FPCMsgFile) -h
// fpc -Fr$(FPCMsgFile) -i
var var
OutputI: TStringList;
OutputH: TStringList; OutputH: TStringList;
begin begin
if fCompilerExecutable = '' then if fCompilerExecutable = '' then
fCompilerExecutable := 'fpc'; // Let's hope "fpc" is found in PATH. fCompilerExecutable := 'fpc'; // Let's hope "fpc" is found in PATH.
try
ReadCompiler(fCompilerExecutable, fParsedTarget, OutputI, OutputH);
Result:=ParseOptions(OutputI, OutputH);
finally
OutputI.Free;
OutputH.Free;
end;
end;
function TCompilerOptReader.ParseOptions(OutputI, OutputH: TStringList): TModalResult;
begin
OptionIdCounter := 0; OptionIdCounter := 0;
fErrorMsg := ''; fErrorMsg := '';
if OutputI = Nil then Exit(mrCancel); try
Result := ParseI(OutputI); // FPC with option -h
if Result <> mrOK then Exit; OutputH := RunTool(fCompilerExecutable, fParsedTarget + ' -h');
if OutputH = Nil then Exit(mrCancel); if OutputH = Nil then Exit(mrCancel);
Result := ParseH(OutputH); Result := ParseH(OutputH);
end; finally
OutputH.Free;
procedure TCompilerOptReader.ReadCompiler(CompPath, Params: string; end;
out OutputI, OutputH: TStringList);
begin
OutputI:=nil;
OutputH:=nil;
if CompPath = '' then
CompPath := 'fpc'; // Let's hope "fpc" is found in PATH.
// FPC with option -i
OutputI:=RunTool(CompPath, Params + ' -i');
// FPC with option -h
OutputH:=RunTool(CompPath, Params + ' -h');
end; end;
function TCompilerOptReader.FilterOptions(aFilter: string; aOnlySelected: Boolean): Boolean; function TCompilerOptReader.FilterOptions(aFilter: string; aOnlySelected: Boolean): Boolean;
@ -1345,8 +1396,7 @@ end;
procedure TCompilerOptThread.Clear; procedure TCompilerOptThread.Clear;
begin begin
FreeAndNil(fOutputH); ;
FreeAndNil(fOutputI);
end; end;
procedure TCompilerOptThread.StartParsing; procedure TCompilerOptThread.StartParsing;
@ -1355,8 +1405,6 @@ begin
WaitFor; WaitFor;
fReader.CompilerExecutable:=LazarusIDE.GetFPCompilerFilename; fReader.CompilerExecutable:=LazarusIDE.GetFPCompilerFilename;
fReader.UpdateTargetParam; fReader.UpdateTargetParam;
fCompPath:=fReader.CompilerExecutable;
fCompParams:=fReader.ParsedTarget;
Start; Start;
fStartedOnce:=true; fStartedOnce:=true;
end; end;
@ -1365,15 +1413,6 @@ procedure TCompilerOptThread.EndParsing;
begin begin
if fStartedOnce then if fStartedOnce then
WaitFor; WaitFor;
if (fOutputI<>nil) or (fOutputH<>nil) then begin
try
fReader.ParseOptions(fOutputI,fOutputH);
except
on E: Exception do
fReader.ErrorMsg := 'Error parsing compiler output: '+E.Message;
end;
Clear;
end;
end; end;
procedure TCompilerOptThread.Execute; procedure TCompilerOptThread.Execute;
@ -1382,9 +1421,7 @@ var
begin begin
StartTime := Now; StartTime := Now;
try try
if fOutputI<>nil then exit; fReader.ReadAndParseOptions;
if fOutputH<>nil then exit;
fReader.ReadCompiler(fCompPath,fCompParams,fOutputI,fOutputH);
except except
on E: Exception do on E: Exception do
fReader.ErrorMsg := 'Error reading compiler: '+E.Message; fReader.ErrorMsg := 'Error reading compiler: '+E.Message;