mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-07 06:08:22 +02:00
412 lines
12 KiB
ObjectPascal
412 lines
12 KiB
ObjectPascal
unit mkfpdoc;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$WARN 5024 off : Parameter "$1" not used}
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, dglobals, DOM, fpdocxmlopts, dwriter, pscanner, pparser, fpdocproj;
|
|
|
|
const
|
|
DefOSTarget = {$I %FPCTARGETOS%};
|
|
DefCPUTarget = {$I %FPCTARGETCPU%};
|
|
DefFPCVersion = {$I %FPCVERSION%};
|
|
DefFPCDate = {$I %FPCDATE%};
|
|
{$IFDEF FPC_BIG_ENDIAN}
|
|
DefEndianNess = 'FPC_BIG_ENDIAN';
|
|
{$ELSE}
|
|
DefEndianNess = 'FPC_LITTLE_ENDIAN';
|
|
{$ENDIF}
|
|
|
|
Type
|
|
|
|
{ TFPDocCreator }
|
|
|
|
TFPDocCreator = Class(TComponent)
|
|
Private
|
|
FBaseDescrDir: String;
|
|
FBaseInputDir: String;
|
|
FCurPackage : TFPDocPackage;
|
|
FExamplesPath: String;
|
|
FProcessedUnits : TStrings;
|
|
FOnLog: TPasParserLogHandler;
|
|
FPParserLogEvents: TPParserLogEvents;
|
|
FProject : TFPDocProject;
|
|
FProjectMacros: TStrings;
|
|
FScannerLogEvents: TPScannerLogEvents;
|
|
FVerbose: Boolean;
|
|
function GetLogLevels: TFPDocLogLevels;
|
|
function GetOptions: TEngineOptions;
|
|
function GetPackages: TFPDocPackages;
|
|
procedure SetBaseDescrDir(AValue: String);
|
|
procedure SetBaseInputDir(AValue: String);
|
|
procedure SetExamplesPath(AValue: String);
|
|
procedure SetProjectMacros(AValue: TStrings);
|
|
Protected
|
|
Function FixInputFile(Const AFileName : String) : String;
|
|
Function FixDescrFile(Const AFileName : String) : String;
|
|
Procedure DoBeforeEmitNote(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean); virtual;
|
|
procedure HandleOnParseUnit(Sender: TObject; const AUnitName: String; out AInputFile, OSTarget, CPUTarget: String);
|
|
procedure SetVerbose(AValue: Boolean); virtual;
|
|
Procedure DoLog(Const Msg : String);
|
|
procedure DoLog(Const Fmt : String; Args : Array of Const);
|
|
Procedure DoLogSender(Sender : TObject; Const Msg : String);
|
|
// Create documetation by specified Writer class
|
|
procedure CreateOutput(APackage: TFPDocPackage; Engine: TFPDocEngine); virtual;
|
|
Public
|
|
Constructor Create(AOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Procedure CreateDocumentation(APackage : TFPDocPackage; ParseOnly : Boolean); virtual; //Writes out documentation in selected format
|
|
Procedure CreateProjectFile(Const AFileName : string); //Writes out project file with the chosen options
|
|
Procedure LoadProjectFile(Const AFileName: string);
|
|
Property Project : TFPDocProject Read FProject;
|
|
Property ScannerLogEvents : TPScannerLogEvents Read FScannerLogEvents Write FScannerLogEvents;
|
|
Property ParserLogEvents : TPParserLogEvents Read FPParserLogEvents Write FPParserLogEvents;
|
|
Property Verbose : Boolean Read FVerbose Write SetVerbose;
|
|
Property OnLog : TPasParserLogHandler Read FOnLog Write FOnLog;
|
|
// Easy access
|
|
Property Options : TEngineOptions Read GetOptions;
|
|
Property Packages : TFPDocPackages Read GetPackages;
|
|
// When set, they will be prepended to non-absolute filenames.
|
|
Property BaseInputDir : String Read FBaseInputDir Write SetBaseInputDir;
|
|
Property BaseDescrDir : String Read FBaseDescrDir Write SetBaseDescrDir;
|
|
Property ExamplesPath : String Read FExamplesPath Write SetExamplesPath;
|
|
// Macros used when loading the project file
|
|
Property ProjectMacros : TStrings Read FProjectMacros Write SetProjectMacros;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses fpdocstrs;
|
|
|
|
{ TFPDocCreator }
|
|
|
|
procedure TFPDocCreator.SetVerbose(AValue: Boolean);
|
|
begin
|
|
if FVerbose=AValue then Exit;
|
|
FVerbose:=AValue;
|
|
if FVerbose then
|
|
begin
|
|
ScannerLogEvents:=[sleFile];
|
|
ParserLogEvents:=[];
|
|
Options.InfoUsedFile:= true;
|
|
Options.WarnDocumentationEmpty:= true;
|
|
Options.WarnXCT:= true;
|
|
end
|
|
else
|
|
begin
|
|
ScannerLogEvents:=[];
|
|
ParserLogEvents:=[];
|
|
end;
|
|
end;
|
|
|
|
procedure TFPDocCreator.DoLog(const Msg: String);
|
|
begin
|
|
If Assigned(OnLog) then
|
|
OnLog(Self,Msg);
|
|
end;
|
|
|
|
procedure TFPDocCreator.DoLog(const Fmt: String; Args: array of const);
|
|
begin
|
|
DoLog(Format(Fmt,Args));
|
|
end;
|
|
|
|
procedure TFPDocCreator.DoLogSender ( Sender: TObject; const Msg: String ) ;
|
|
begin
|
|
if Assigned(Sender) then
|
|
DoLog(Format('%s - Sender: %s', [Msg, Sender.ClassName]))
|
|
else
|
|
DoLog(Msg);
|
|
end;
|
|
|
|
procedure TFPDocCreator.HandleOnParseUnit(Sender: TObject;
|
|
const AUnitName: String; out AInputFile, OSTarget, CPUTarget: String);
|
|
|
|
Var
|
|
I : Integer;
|
|
S,un,opts : String;
|
|
|
|
begin
|
|
AInputFile:='';
|
|
OSTarget:='';
|
|
CPUTarget:='';
|
|
if Assigned(FCurPackage) then
|
|
begin
|
|
I:=0;
|
|
While (AInputFIle='') and (I<FCurPackage.Inputs.Count) do
|
|
begin
|
|
S:=FCurPackage.Inputs[i];
|
|
SplitInputFIleOption(S,UN,Opts);
|
|
if CompareText(ChangeFileExt(ExtractFileName(Un),''),AUnitName)=0 then
|
|
begin
|
|
AInputFile:=FixInputFile(UN)+' '+Opts+' -d'+Options.EndianNess;
|
|
OSTarget:=FProject.Options.OSTarget;
|
|
CPUTarget:=FProject.Options.CPUTarget;
|
|
FProcessedUnits.Add(UN);
|
|
end;
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TFPDocCreator.GetOptions: TEngineOptions;
|
|
begin
|
|
Result:=FProject.Options;
|
|
end;
|
|
|
|
function TFPDocCreator.GetPackages: TFPDocPackages;
|
|
begin
|
|
Result:=FProject.Packages;
|
|
end;
|
|
|
|
function TFPDocCreator.FixInputFile(const AFileName: String): String;
|
|
begin
|
|
Result:=AFileName;
|
|
If Result='' then exit;
|
|
if (ExtractFileDrive(Result)='') and (Result[1]<>PathDelim) then
|
|
Result:=BaseInputDir+Result;
|
|
end;
|
|
|
|
function TFPDocCreator.FixDescrFile(const AFileName: String): String;
|
|
begin
|
|
Result:=AFileName;
|
|
If Result='' then exit;
|
|
if (ExtractFileDrive(Result)='') and (Result[1]<>PathDelim) then
|
|
Result:=BaseDescrDir+Result;
|
|
end;
|
|
|
|
procedure TFPDocCreator.SetBaseDescrDir(AValue: String);
|
|
begin
|
|
if FBaseDescrDir=AValue then Exit;
|
|
FBaseDescrDir:=AValue;
|
|
If FBaseDescrDir<>'' then
|
|
FBaseDescrDir:=IncludeTrailingPathDelimiter(FBaseDescrDir);
|
|
end;
|
|
|
|
procedure TFPDocCreator.SetBaseInputDir(AValue: String);
|
|
begin
|
|
if FBaseInputDir=AValue then Exit;
|
|
FBaseInputDir:=AValue;
|
|
If FBaseInputDir<>'' then
|
|
FBaseInputDir:=IncludeTrailingPathDelimiter(FBaseInputDir);
|
|
end;
|
|
|
|
procedure TFPDocCreator.SetExamplesPath(AValue: String);
|
|
begin
|
|
if FExamplesPath=AValue then Exit;
|
|
FExamplesPath:=AValue;
|
|
If FExamplesPath<>'' then
|
|
FExamplesPath:=IncludeTrailingPathDelimiter(FExamplesPath);
|
|
end;
|
|
|
|
procedure TFPDocCreator.SetProjectMacros(AValue: TStrings);
|
|
begin
|
|
if FProjectMacros=AValue then Exit;
|
|
FProjectMacros.Assign(AValue);
|
|
end;
|
|
|
|
procedure TFPDocCreator.DoBeforeEmitNote(Sender: TObject; Note: TDomElement;
|
|
var EmitNote: Boolean);
|
|
begin
|
|
EmitNote:=True;
|
|
end;
|
|
|
|
constructor TFPDocCreator.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FProject:=TFPDocProject.Create(Self);
|
|
FProject.Options.StopOnParseError:=False;
|
|
FProject.Options.CPUTarget:=DefCPUTarget;
|
|
FProject.Options.OSTarget:=DefOSTarget;
|
|
FProject.Options.EndianNess:=DefEndianNess;
|
|
FProcessedUnits:=TStringList.Create;
|
|
FProjectMacros:=TStringList.Create;
|
|
end;
|
|
|
|
destructor TFPDocCreator.Destroy;
|
|
begin
|
|
FreeAndNil(FProcessedUnits);
|
|
FreeAndNil(FProject);
|
|
FreeAndNil(FProjectMacros);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TFPDocCreator.CreateOutput(APackage: TFPDocPackage;Engine : TFPDocEngine);
|
|
|
|
Var
|
|
WriterClass : TFPDocWriterClass;
|
|
Writer : TFPDocWriter;
|
|
I : Integer;
|
|
Cmd,Arg : String;
|
|
|
|
begin
|
|
// Now is used the specified writer
|
|
WriterClass:=GetWriterClass(Options.Backend);
|
|
// ALL CONTENT CREATED HERE
|
|
Writer:=WriterClass.Create(Engine.Package,Engine);
|
|
With Writer do
|
|
Try
|
|
If FVerbose then
|
|
DoLog('Writing documentation');
|
|
OnLog:=Self.OnLog;
|
|
BeforeEmitNote:=@self.DoBeforeEmitNote;
|
|
EmitNotes:=Options.EmitNotes;
|
|
If Options.BackendOptions.Count>0 then
|
|
for I:=0 to ((Options.BackendOptions.Count-1) div 2) do
|
|
begin
|
|
Cmd:=Options.BackendOptions[I*2];
|
|
Arg:=Options.BackendOptions[I*2+1];
|
|
If not InterPretOption(Cmd,Arg) then
|
|
DoLog(SCmdLineInvalidOption,[Cmd+'='+Arg]);
|
|
end;
|
|
// Create documentation by writer
|
|
WriteDocumentation();
|
|
Finally
|
|
Free;
|
|
end;
|
|
// Output content files
|
|
if FVerbose then
|
|
DoLog('Content file : '+APackage.ContentFile);
|
|
if Length(APackage.ContentFile) > 0 then
|
|
Engine.WriteContentFile(APackage.ContentFile);
|
|
end;
|
|
|
|
function TFPDocCreator.GetLogLevels: TFPDocLogLevels;
|
|
|
|
Procedure DoOpt(doSet : Boolean; aLevel: TFPDocLogLevel);
|
|
|
|
begin
|
|
if DoSet then
|
|
Result:=Result+[aLevel];
|
|
end;
|
|
|
|
begin
|
|
Result:=[];
|
|
DoOpt(Options.WarnNoNode,dleWarnNoNode);
|
|
DoOpt(Options.InfoUsedFile,dleWarnUsedFile);
|
|
DoOpt(Options.WarnDocumentationEmpty,dleDocumentationEmpty);
|
|
DoOpt(Options.WarnXCT,dleXCT);
|
|
end;
|
|
|
|
procedure TFPDocCreator.CreateDocumentation(APackage: TFPDocPackage;
|
|
ParseOnly: Boolean);
|
|
|
|
var
|
|
i,j: Integer;
|
|
Engine : TFPDocEngine;
|
|
Cmd,Arg : String;
|
|
WriterClass: TFPDocWriterClass;
|
|
eMsg: String;
|
|
begin
|
|
Cmd:='';
|
|
FCurPackage:=APackage;
|
|
Engine:=TFPDocEngine.Create;
|
|
try
|
|
Engine.OnLog:= @DoLogSender;
|
|
Engine.ExamplesPath:=Self.ExamplesPath;
|
|
// get documentation Writer html, latex, and other
|
|
WriterClass:=GetWriterClass(Options.Backend);
|
|
For J:=0 to Apackage.Imports.Count-1 do
|
|
begin
|
|
Arg:=Apackage.Imports[j];
|
|
// conversion import FilePathes
|
|
WriterClass.SplitImport(Arg,Cmd);
|
|
// create tree of imported objects
|
|
Engine.ReadContentFile(Arg, Cmd);
|
|
end;
|
|
for i := 0 to APackage.Descriptions.Count - 1 do
|
|
Engine.AddDocFile(FixDescrFile(APackage.Descriptions[i]),Options.donttrim);
|
|
// set engine options
|
|
Engine.SetPackageName(APackage.Name);
|
|
Engine.Output:=APackage.Output;
|
|
Engine.OnLog:=Self.OnLog;
|
|
Engine.ScannerLogEvents:=Self.ScannerLogEvents;
|
|
Engine.ParserLogEvents:=Self.ParserLogEvents;
|
|
Engine.HideProtected:=Options.HideProtected;
|
|
Engine.HidePrivate:=Not Options.ShowPrivate;
|
|
Engine.OnParseUnit:=@HandleOnParseUnit;
|
|
Engine.DocLogLevels:=GetLogLevels;
|
|
Engine.FalbackSeeAlsoLinks:= Options.FallBackSeeAlsoLinks;
|
|
if Length(Options.Language) > 0 then
|
|
TranslateDocStrings(Options.Language);
|
|
// scan the input source files
|
|
for i := 0 to APackage.Inputs.Count - 1 do
|
|
try
|
|
try
|
|
eMsg:='';
|
|
// get options from input packages
|
|
SplitInputFileOption(APackage.Inputs[i],Cmd,Arg);
|
|
arg:=Arg+' -d'+Options.EndianNess;
|
|
// make absolute filepath
|
|
Cmd:=FixInputFile(Cmd);
|
|
if FProcessedUnits.IndexOf(Cmd)=-1 then
|
|
begin
|
|
FProcessedUnits.Add(Cmd);
|
|
// Parce sources for OS Target
|
|
//WriteLn(Format('Parsing unit: %s', [ExtractFilenameOnly(Cmd)]));
|
|
ParseSource(Engine,Cmd+' '+Arg, Options.OSTarget, Options.CPUTarget,[poUseStreams]); // poSkipDefaultDefs
|
|
end;
|
|
//else WriteLn(Format('Processed unit: %s', [ExtractFilenameOnly(Cmd)]));
|
|
except
|
|
on E: EParserError do
|
|
begin
|
|
eMsg:= Format('Parser error: %s (%d,%d): %s',[E.Filename, E.Row, E.Column, E.Message]);
|
|
If Options.StopOnParseError then Raise;
|
|
end;
|
|
on E: EFileNotFoundError do
|
|
begin
|
|
eMsg:= Format('Error: file not found - %s', [E.Message]);
|
|
If Options.StopOnParseError then Raise;
|
|
end;
|
|
on E: Exception do
|
|
begin
|
|
eMsg:= Format('Error: %s', [E.Message]);
|
|
If Options.StopOnParseError then Raise;
|
|
end;
|
|
end; // try except
|
|
finally
|
|
if eMsg <> '' then
|
|
begin
|
|
DoLog(eMsg);
|
|
If not Options.StopOnParseError then
|
|
DoLog('Ignoring error, continuing with next unit (if any).');
|
|
end;
|
|
end; // try finally
|
|
if Not ParseOnly then
|
|
begin
|
|
Engine.StartDocumenting;
|
|
// Create documentation
|
|
CreateOutput(APackage,Engine);
|
|
end;
|
|
finally
|
|
FreeAndNil(Engine);
|
|
FCurPackage:=Nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPDocCreator.CreateProjectFile(const AFileName: string);
|
|
begin
|
|
With TXMLFPDocOptions.Create(Self) do
|
|
try
|
|
SaveOptionsToFile(FProject,AFileName);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TFPDocCreator.LoadProjectFile(const AFileName: string);
|
|
begin
|
|
With TXMLFPDocOptions.Create(self) do
|
|
try
|
|
if (ProjectMacros.Count>0) then
|
|
LoadOptionsFromFile(FProject,AFileName,ProjectMacros)
|
|
else
|
|
LoadOptionsFromFile(FProject,AFileName,Nil);
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|