Michael Van Canneyt schrieb:
You remember my -n and -v options?
Absolutely, and I'll gladly accept patches implementing those 2 things
in fpdoc.
If you could separate those out from your other work, that would be much
appreciated. If not, send whatever you have, and I'll try to extract it
myself.
I'll attach an patch with my remaining changes, fpdoc-nv.patch. Here's a
roadmap for you:
I've enclosed most still different parts of the code with conditional
DoDi. This should allow (me) to run both versions from the same source,
and helps in searching for differences.
pparser.pp is extended with a -v option, passed as commandline argument.
It writes out the currently processed unit. Not perfect, of course, it
would be more useful when the current unit were shown with every error
message, by default.
pscanner.pp is even more nasty, I could not find an easy way to
implement a -v (or -vt) option. That's why I added a conditional
"Verbose", that turns on the trace of tried include directories. Perhaps
you find an better way, using the settings of pparser.
IMO this option is essential for the fpdoc compiler (--input) options
setup in every project. I'd be happy if you can provide common package
options, like
<package name="bla" options="-Fi common to all units"/>
which is automatically appended to all unit options.
dGlobals.pp contains the Verbose flag, for use in fpdoc. And the help
messages, of course.
AddDocFile gets an additional package name, that allows to exclude
unrelated docs, and also does wildcard substitution.
dw_html.pp traces the created HTML files. Like with pparser, the element
name (html file) should be shown with *every* error message instead.
fpdoc.pp obviously accepts the added commandline options. It also
creates a default project whenever invoked without a project.
fpdocproj.pas Options.DryRun should be obvious.
Improved --imports handling, so that the prefix is no more required (can
be synthesized). This part can be ommitted.
Moved CreateDocumentation here, to make it independent from commandline
handling.
fpdocxmlopts.pas can be almost ignored, it retains my old save/load code
(see DoDiSave), until I'm sure that I added all required modifications
to your code. Thanks for extracting the file names from the options :-)
Have fun :-)
DoDi
Index: packages/fcl-passrc/src/pparser.pp
===================================================================
--- packages/fcl-passrc/src/pparser.pp (revision 19730)
+++ packages/fcl-passrc/src/pparser.pp (working copy)
@@ -304,6 +304,7 @@
Start, CurPos: PChar;
Filename: String;
Scanner: TPascalScanner;
+ Verbose: boolean; //should become a global option!?
procedure ProcessCmdLinePart;
var
@@ -332,6 +333,7 @@
include(Scanner.Options,po_delphi);
Parser.Options:=Parser.Options+[po_delphi];
end;
+ 'v': Verbose := True;
end;
end else
if Filename <> '' then
@@ -347,6 +349,7 @@
FileResolver := nil;
Scanner := nil;
Parser := nil;
+ Verbose := False;
try
FileResolver := TFileResolver.Create;
Scanner := TPascalScanner.Create(FileResolver);
@@ -407,6 +410,9 @@
if Filename = '' then
raise Exception.Create(SErrNoSourceGiven);
+ if Verbose then
+ WriteLn('Now parsing ', Filename);
+
Scanner.OpenFile(Filename);
Parser.ParseMain(Result);
finally
Index: packages/fcl-passrc/src/pscanner.pp
===================================================================
--- packages/fcl-passrc/src/pscanner.pp (revision 19730)
+++ packages/fcl-passrc/src/pscanner.pp (working copy)
@@ -17,6 +17,8 @@
{$mode objfpc}
{$h+}
+{.$DEFINE Verbose} //should become a property
+
unit PScanner;
interface
@@ -466,6 +468,10 @@
If FileExists(Result) then exit;
Result:=Dir+uppercase(Fn);
If FileExists(Result) then exit;
+{$IFDEF Verbose}
+ //if Verbose then
+ WriteLn('Tried: ', Result);
+{$ENDIF}
Result:='';
end;
end;
@@ -494,13 +500,22 @@
begin
Try
FN:=SearchLowUpCase(FIncludePaths[i]+AName);
- If (FN<>'') then
+ If (FN<>'') then begin
+{$IFDEF Verbose}
+ //if Verbose then
+ WriteLn('Found: ', FN);
+{$ENDIF}
Result := TFileLineReader.Create(FN);
+ end;
except
Result:=Nil;
end;
Inc(I);
end;
+
+ if Result <> nil then
+ exit; //succ!
+
// search in BaseDirectory
if BaseDirectory<>'' then
begin
Index: utils/fpdoc/dglobals.pp
===================================================================
--- utils/fpdoc/dglobals.pp (revision 19730)
+++ utils/fpdoc/dglobals.pp (working copy)
@@ -28,6 +28,7 @@
Var
LEOL : Integer;
modir : string;
+ Verbose: boolean = False; //debug option
resourcestring
// Output strings
@@ -149,7 +150,9 @@
SUsageOption170 = '--warn-no-node Warn if no documentation node was
found.';
SUsageOption180 = '--mo-dir=dir Set directory where language files
reside to dir';
SUsageOption190 = '--parse-impl (Experimental) try to parse
implementation too';
- SUsageOption200 = '--dont-trim Don''t trim XML contents';
+ SUsageOption200 = '--dont-trim Don''t trim XML contents';
+ SUsageOption201 = '-n Don''t create documents';
+ SUsageOption202 = '-v Verbose, you better redirect
output into an logfile';
SUsageOption210 = '--write-project=file Do not write documentation, create
project file instead';
SUsageFormats = 'The following output formats are supported by this
fpdoc:';
@@ -303,7 +306,11 @@
function FindLinkedNode(ANode: TDocNode): TDocNode;
// Documentation file support
- procedure AddDocFile(const AFilename: String;DontTrim:boolean=false);
+{$IFDEF DoDi}
+ function AddDocFile(const AFilename, APackageName:
String;DontTrim:boolean=false): boolean;
+{$ELSE}
+ function AddDocFile(const AFilename: String;DontTrim:boolean=false):
boolean;
+{$ENDIF}
// Documentation retrieval
function FindDocNode(AElement: TPasElement): TDocNode;
@@ -1285,7 +1292,11 @@
end;
end;
-procedure TFPDocEngine.AddDocFile(const AFilename:
String;DontTrim:boolean=false);
+{$IFDEF DoDi}
+function TFPDocEngine.AddDocFile(const AFilename, APackageName:
String;DontTrim:boolean=false): boolean;
+{$ELSE}
+function TFPDocEngine.AddDocFile(const AFilename:
String;DontTrim:boolean=false): boolean;
+{$ENDIF}
function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
var
@@ -1346,20 +1357,52 @@
Element: TDOMElement;
Doc: TXMLDocument;
PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
+ UseDoc: boolean;
+//wildcard substitution
+ Info : TSearchRec;
+ pattern, prefix: string;
+begin
+{$IFDEF DoDi}
+(* The document is retained only when it contains the current package.
+ Assume that the names match in case?
-begin
+ Try: resolve wildcards in filename.
+ ToDo: strip duplicate file references, when wildcards and explicit
references are mixed.
+*)
+ i := Pos('*', AFilename);
+ if i > 0 then begin
+ //add all files matching pattern
+ prefix := Copy(AFileName, 1, i-1);
+ pattern := ChangeFileExt(AFilename, '.xml'); //in case no extension is
given
+ if FindFirst(pattern, faAnyFile, Info) = 0 then begin
+ repeat
+ UseDoc := AddDocFile(prefix + Info.Name, APackageName, DontTrim);
+ if Verbose and UseDoc then
+ WriteLn('Using ', Info.Name);
+ until FindNext(info)<>0;
+ FindClose(Info);
+ end;
+ exit(true); //all done, no document loaded here
+ end;
+{$ELSE}
+{$ENDIF}
+
+ UseDoc := False; //becomes true when really required
if DontTrim then
ReadXMLFileALT(Doc, AFilename)
else
ReadXMLFile(Doc, AFilename);
- DescrDocs.Add(Doc);
- DescrDocNames.Add(AFilename);
Node := Doc.DocumentElement.FirstChild;
while Assigned(Node) do
begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
+ if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package')
+ {$IFDEF DoDi}
+ and ((Node as TDOMElement)['name']= APackageName)
+ {$ENDIF}
+ then //only for current package
begin
+ UseDoc:=True; //this document is really used
PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
PackageDocNode.IncRefCount;
// Scan all 'module' elements within this package element
@@ -1401,6 +1444,15 @@
end;
Node := Node.NextSibling;
end;
+ Result := UseDoc;
+ if UseDoc then begin
+ DescrDocs.Add(Doc);
+ DescrDocNames.Add(AFilename);
+ end else begin
+ if Verbose then
+ WriteLn('Skip ', AFilename);
+ Doc.Free; //more to release?
+ end;
end;
function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
Index: utils/fpdoc/dw_html.pp
===================================================================
--- utils/fpdoc/dw_html.pp (revision 19730)
+++ utils/fpdoc/dw_html.pp (working copy)
@@ -735,6 +735,8 @@
try
Filename := Engine.Output + Allocator.GetFilename(Element,
SubpageIndex);
try
+ if dGlobals.Verbose then
+ WriteLn('Create ', Filename);
CreatePath(Filename);
WriteHTMLFile(PageDoc, Filename);
except
Index: utils/fpdoc/fpdoc.pp
===================================================================
--- utils/fpdoc/fpdoc.pp (revision 19730)
+++ utils/fpdoc/fpdoc.pp (working copy)
@@ -12,6 +12,7 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
+{$DEFINE CreateProject} //always create an project file?
program FPDoc;
@@ -42,15 +43,23 @@
TFPDocAplication = Class(TCustomApplication)
private
FProject : TFPDocProject;
- FProjectFile : Boolean;
+ {$IFDEF DoDi}
+ FXMLProject: TXMLFPDocOptions;
+ {$ELSE}
+ FProjectFile: boolean;
+ {$ENDIF}
FPackage : TFPDocPackage;
FWriteProjectFile : String;
Protected
procedure ParseCommandLine;
procedure Parseoption(const S: String);
Procedure Usage(AnExitCode : Byte);
+ {$IFDEF DoDi}
+ //enable new CreateProjectFile
+ {$ELSE}
Procedure CreateProjectFile(Const AFileName : String);
procedure CreateDocumentation(APackage : TFPDocPackage; Options :
TEngineOptions);
+ {$ENDIF}
Procedure DoRun; override;
Public
Constructor Create(AOwner : TComponent); override;
@@ -90,6 +99,11 @@
Writeln(SUsageOption180);
Writeln(SUsageOption190);
Writeln(SUsageOption200);
+{$IFDEF DoDi}
+ Writeln(SUsageOption201);
+ Writeln(SUsageOption202);
+{$ELSE}
+{$ENDIF}
Writeln(SUsageOption210);
L:=TStringList.Create;
Try
@@ -126,6 +140,8 @@
Halt(AnExitCode);
end;
+{$IFDEF DoDi}
+{$ELSE}
procedure TFPDocAplication.CreateProjectFile(const AFileName: String);
begin
With TXMLFPDocOptions.Create(Self) do
@@ -135,11 +151,15 @@
Free;
end;
end;
+{$ENDIF}
destructor TFPDocAplication.Destroy;
begin
FreeAndNil(FProject);
+{$IFDEF DoDi}
+ FreeAndNil(FXMLProject);
+{$ENDIF}
Inherited;
end;
@@ -178,16 +198,17 @@
begin
s:=ParamStr(I);
If ProjectOpt(S) then
+ begin
ParseOption(s);
- If (FProject.Packages.Count=1) then
- FPackage:=FProject.Packages[0]
- else if (FProject.Options.DefaultPackageName<>'') then
-
Fpackage:=FProject.Packages.FindPackage(FProject.Options.DefaultPackageName);
+ If FProject.Packages.Count>0 then
+ begin
+
Fpackage:=FProject.Packages.FindPackage(FProject.Options.DefaultPackageName);
+ if FPackage = nil then
+ FPackage:=FProject.Packages[0];
+ break; //ignore further projects? (else handle first instance when
another project was given)
+ end;
+ end;
end;
- If FProject.Packages.Count=0 then
- begin
- FPackage:=FProject.Packages.Add as TFPDocPackage;
- end;
// Check package
for i := 1 to ParamCount do
begin
@@ -195,13 +216,30 @@
If PackageOpt(S) then
ParseOption(s);
end;
+
+ if (FPackage=Nil) or (FPackage.Name='') then
+ begin
+ Writeln(SNeedPackageName);
+ Usage(1); //also show help, not only error
+ end;
+
for i := 1 to ParamCount do
begin
s:=ParamStr(I);
If Not (ProjectOpt(s) or PackageOpt(S)) then
ParseOption(s);
end;
+{$IFDEF DoDi}
+//always create project, when none was given (allow for use in scripts!)
+ if not assigned(FXMLProject) and assigned(SelectedPackage) then begin
+ FXMLProject := TXMLFPDocOptions.Create(self, SelectedPackage.Name +
'_auto.xml');
+ FXMLProject.Modified := True; //force save
+ end;
+ if assigned(FXMLProject) then
+ FXMLProject.SaveProject(FProject);
+{$ELSE}
SelectedPackage; // Will print error if none available.
+{$ENDIF}
end;
procedure TFPDocAplication.Parseoption(Const S : String);
@@ -228,7 +266,6 @@
var
i: Integer;
Cmd, Arg: String;
-
begin
if (s = '-h') or (s = '--help') then
Usage(0)
@@ -242,6 +279,11 @@
FProject.Options.StopOnParseError := True
else if s = '--dont-trim' then
FProject.Options.donttrim := True
+ else if s = '-v' then
+ //FProject.Options.Verbose := True
+ dGlobals.Verbose:=True
+ else if s = '-n' then
+ FProject.Options.DryRun := True
else
begin
i := Pos('=', s);
@@ -257,6 +299,15 @@
end;
if (Cmd = '--project') or (Cmd='-p') then
begin
+ {$IFDEF DoDi}//todo: fix!
+ if FXMLProject <> nil then
+ begin
+ //error: only 1 project
+ exit;
+ end;
+ FXMLProject := TXMLFPDocOptions.Create(self, Arg);
+ FXMLProject.LoadProject(FProject);
+ {$ELSE}
FProjectFile:=True;
With TXMLFPDocOptions.Create(self) do
try
@@ -264,6 +315,7 @@
finally
Free;
end;
+ {$ENDIF}
end
else if (Cmd = '--descr') then
AddToFileList(SelectedPackage.Descriptions, Arg)
@@ -286,10 +338,12 @@
else if Cmd = '--import' then
SelectedPackage.Imports.Add(Arg)
else if Cmd = '--package' then
- begin
- If FProjectFile then
- FPackage:=FProject.Packages.FindPackage(Arg)
- else
+ begin //scanned after project, selects a package
+ FPackage:=FProject.Packages.FindPackage(Arg);
+ if assigned(FPackage) then
+ exit; //all done
+ //create new package
+ FPackage:=FProject.Packages.Add as TFPDocPackage;
FPackage.Name:=Arg;
end
else if Cmd = '--ostarget' then
@@ -310,7 +364,8 @@
end;
end;
-
+{$IFDEF DoDi}
+{$ELSE}
procedure TFPDocAplication.CreateDocumentation(APackage : TFPDocPackage;
Options : TEngineOptions);
var
@@ -348,6 +403,8 @@
WriteLn(StdErr, Format('%s(%d,%d): %s',
[e.Filename, e.Row, e.Column, e.Message]));
end;
+ if Options.DryRun then
+ exit; //don't produce output
WriterClass:=GetWriterClass(Options.Backend);
Writer:=WriterClass.Create(Engine.Package,Engine);
Writeln('Writing doc');
@@ -371,6 +428,7 @@
FreeAndNil(Engine);
end;
end;
+{$ENDIF}
Procedure TFPDocAplication.DoRun;
@@ -386,10 +444,14 @@
WriteLn(SCopyright);
WriteLn;
ParseCommandLine;
+{$IFDEF DoDi}
+ FProject.CreateDocumentation(FPackage);
+{$ELSE}
if (FWriteProjectFile<>'') then
CreateProjectFile(FWriteProjectFile)
else
CreateDocumentation(FPackage,FProject.Options);
+{$ENDIF}
WriteLn(SDone);
Terminate;
end;
@@ -404,6 +466,8 @@
FProject.Options.OSTarget:=DefOSTarget;
end;
+{$R *.res}
+
begin
With TFPDocAplication.Create(Nil) do
try
Index: utils/fpdoc/fpdocproj.pas
===================================================================
--- utils/fpdoc/fpdocproj.pas (revision 19730)
+++ utils/fpdoc/fpdocproj.pas (working copy)
@@ -9,13 +9,26 @@
Type
+ { TFPDocImports }
+
+ TFPDocImports = class(TStringList)
+ protected
+ //function Get(index: integer): string; override;
+ function Add(const S: string): Integer; override;
+ //procedure Put(index: integer; const s: string); override;
+ public
+ function ImportName(const s: string): string;
+ property Strings[index: integer]: string read GetValueFromIndex write Put;
+ end;
+
{ TFPDocPackage }
TFPDocPackage = Class(TCollectionItem)
private
FContent: String;
+ FDescDir: string;
FDescriptions: TStrings;
- FIMports: TStrings;
+ FIMports: TFPDocImports;
FinPuts: TStrings;
FName: String;
FOutput: String;
@@ -24,9 +37,10 @@
destructor destroy; override;
procedure Assign(Source : TPersistent); override;
Property Name : String Read FName Write FName;
+ Property Imports : TFPDocImports read FIMports;
Property Inputs : TStrings Read FinPuts;
Property Descriptions : TStrings Read FDescriptions;
- Property Imports : TStrings read FIMports;
+ property DescDir: string read FDescDir write FDescDir;
Property ContentFile : String Read FContent Write FContent;
Property Output : String Read FOutput Write FOutput;
end;
@@ -65,6 +79,8 @@
Constructor Create;
Destructor Destroy; override;
procedure Assign(Source : TPersistent); override;
+ public //debug options
+ DryRun: boolean;
Published
Property OSTarget : String Read FOSTarget Write FOStarget;
Property CPUTarget : String Read FCPUTarget Write FCPUTarget;
@@ -91,6 +107,10 @@
Public
Constructor Create(AOwner : TComponent); override;
Destructor Destroy; override;
+ {$IFDEF DoDi}
+ procedure CreateDocumentation(APackage : TFPDocPackage);
+ {$ELSE}
+ {$ENDIF}
Published
Property Packages : TFPDocPackages Read FPackages Write FPackages;
Property Options : TEngineOptions Read FOptions Write setOptions;
@@ -98,6 +118,38 @@
implementation
+uses
+ dWriter, dGlobals, PParser;
+
+{ TFPDocImports }
+
+function TFPDocImports.Add(const s: string): integer;
+var
+ pkg: string;
+begin
+//Add either a package name (only), or pkg=s
+ pkg := ImportName(s);
+ Result := IndexOfName(pkg);
+ if Result < 0 then
+ Result := inherited Add(pkg);
+ if s <> pkg then
+ Strings[Result] := pkg + '=' + s;
+end;
+
+function TFPDocImports.ImportName(const s: string): string;
+//function TFPDocPackage.ImportName(const s: string): string;
+var
+ p: integer;
+ s2: string;
+begin
+//extract the package name from an --imports option
+ s2 := s;
+ p := pos(',', s2);
+ if p > 0 then
+ SetLength(s2, p-1);
+ Result := ChangeFileExt(ExtractFileName(s2), '');
+end;
+
{ TEngineOptions }
procedure TEngineOptions.SetBackendOptions(const AValue: TStrings);
@@ -164,6 +216,79 @@
inherited Destroy;
end;
+{$IFDEF DoDi}
+//beware: sync with TFPDocAplication.CreateDocumentation
+procedure TFPDocProject.CreateDocumentation(APackage: TFPDocPackage);
+var
+ i,j: Integer;
+ WriterClass : TFPDocWriterClass;
+ Writer : TFPDocWriter;
+ Engine : TFPDocEngine;
+ Cmd,Arg : String;
+begin
+ Engine:=TFPDocEngine.Create;
+ try
+ For J:=0 to Apackage.Imports.Count-1 do
+ begin
+ Arg:=Apackage.Imports[j];
+ i := Pos(',', Arg);
+ if i <= 0 then
+ raise EInOutError('Missing context file for ' +
APackage.Imports.Names[j])
+ else
+ Engine.ReadContentFile(Copy(Arg,1,i-1),Copy(Arg,i+1,Length(Arg)));
+ end;
+ for i := 0 to APackage.Descriptions.Count - 1 do
+ Engine.AddDocFile(APackage.Descriptions[i],
APackage.Name,Options.donttrim);
+ Engine.SetPackageName(APackage.Name);
+ Engine.Output:=APackage.Output;
+ Engine.HideProtected:=Options.HideProtected;
+ Engine.HidePrivate:=Not Options.ShowPrivate;
+ if Length(Options.Language) > 0 then
+ TranslateDocStrings(Options.Language);
+ for i := 0 to APackage.Inputs.Count - 1 do
+ try
+ Arg := APackage.Inputs[i];
+ if dGlobals.Verbose then
+ begin
+ Arg := Arg+' -v'; //force verbose parser
+ end;
+ ParseSource(Engine, Arg, Options.OSTarget, Options.CPUTarget);
+ except
+ on e: EParserError do
+ If Options.StopOnParseError then
+ Raise
+ else
+ WriteLn(StdErr, Format('%s(%d,%d): %s',
+ [e.Filename, e.Row, e.Column, e.Message]));
+ end;
+ WriterClass:=GetWriterClass(Options.Backend);
+ Writer:=WriterClass.Create(Engine.Package,Engine);
+ if not Options.DryRun then
+ Writeln('Writing doc');
+ With Writer do
+ Try
+ 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
+ WriteLn(StdErr, Format(SCmdLineInvalidOption,[Cmd+'='+Arg]));
+ end;
+ if not Options.DryRun then
+ WriteDoc;
+ Finally
+ Free;
+ end;
+ if not Options.DryRun and (Length(APackage.ContentFile) > 0) then
+ Engine.WriteContentFile(APackage.ContentFile);
+ finally
+ FreeAndNil(Engine);
+ end;
+end;
+{$ELSE}
+{$ENDIF}
+
{ TFPDocPackages }
function TFPDocPackages.GetP(AIndex : Integer): TFPDocPackage;
@@ -202,7 +327,7 @@
constructor TFPDocPackage.Create(ACollection: TCollection);
begin
inherited Create(ACollection);
- FImports:=TStringList.Create;
+ FImports:=TFPDocImports.Create;
FDescriptions:=TStringList.Create;
FInputs:=TStringList.Create;
end;
Index: utils/fpdoc/fpdocxmlopts.pas
===================================================================
--- utils/fpdoc/fpdocxmlopts.pas (revision 19730)
+++ utils/fpdoc/fpdocxmlopts.pas (working copy)
@@ -16,22 +16,44 @@
FMacros: TStrings;
procedure SetMacros(AValue: TStrings);
Protected
+ Doc: TXMLDocument;
+ FileName: string;
Procedure Error(Const Msg : String);
Procedure Error(Const Fmt : String; Args : Array of Const);
Procedure LoadPackage(APackage : TFPDocPackage; E : TDOMElement); virtual;
Procedure LoadPackages(Packages : TFPDocPackages; E : TDOMElement);
Procedure LoadEngineOptions(Options : TEngineOptions; E : TDOMElement);
virtual;
+{$IFDEF DoDiSave}
+ function OldSaveToXML(AProject : TFPDocProject; ADoc: TXMLDocument):
boolean;
+ function SaveOptions(AProject : TFPDocProject): boolean;
+ function SavePackage(APackage : TFPDocPackage; Pkgs: TDOMElement): boolean;
+ procedure SaveImports(APackage : TFPDocPackage; Pkg: TDOMElement);
+ function NeedNode(P: TDomNode; const ATag: string): TDOMElement;
+ function FindChild(N: TDOMNode; const ATag, AVal: string): TDOMNode;
+{$ELSE}
Procedure SaveEngineOptions(Options : TEngineOptions; XML : TXMLDocument;
AParent : TDOMElement); virtual;
procedure SaveDescription(const ADescription: String; XML: TXMLDocument;
AParent: TDOMElement); virtual;
procedure SaveInputFile(const AInputFile: String; XML: TXMLDocument;
AParent: TDOMElement);virtual;
Procedure SavePackage(APackage : TFPDocPackage; XML : TXMLDocument;
AParent : TDOMElement); virtual;
procedure DoMacro(Sender: TObject; const TagString: String; TagParams:
TStringList; out ReplaceText: String); virtual;
function ExpandMacrosInFile(AFileName: String): TStream; virtual;
+{$ENDIF}
Public
+ Modified: boolean;
+{$IFDEF DoDi}
+ constructor Create(AOwner: TComponent; const AFile: string);
+ //symmetric load/save
+ function LoadProject(AProject : TFPDocProject): boolean;
+ function SaveProject(AProject : TFPDocProject): boolean;
+{$ELSE}
+{$ENDIF}
Constructor Create (AOwner : TComponent); override;
Destructor Destroy; override;
Procedure LoadOptionsFromFile(AProject : TFPDocProject; Const AFileName :
String);
Procedure LoadFromXML(AProject : TFPDocProject; XML : TXMLDocument);
virtual;
+{$IFDEF DoDi}
+{$ELSE}
+{$ENDIF}
Procedure SaveOptionsToFile(AProject : TFPDocProject; Const AFileName :
String);
procedure SaveToXML(AProject : TFPDocProject; ADoc: TXMLDocument); virtual;
Property Macros : TStrings Read FMacros Write SetMacros;
@@ -51,7 +73,7 @@
implementation
-Uses XMLRead, XMLWrite;
+Uses XMLRead, XMLWrite, dGlobals;
Resourcestring
SErrInvalidRootNode = 'Invalid options root node: Got "%s", expected
"docproject"';
@@ -146,6 +168,9 @@
S:=S.NextSibling;
end;
end
+ end else if O.NodeName = 'import' then begin
+ //handle after --import in commandline options
+ APackage.Imports.Add(O['package']);
end;
N:=N.NextSibling;
end;
@@ -455,6 +480,15 @@
XML : TXMLDocument;
begin
+ if not Modified then
+ exit; //don't save unmodified project
+ if AFileName = '' then
+ exit; //no filename?
+ if AFileName = FileName then
+ exit; //don't overwrite
+ if dGlobals.Verbose then
+ WriteLn('Creating FPDoc project ', GetCurrentDir, DirectorySeparator,
AFileName);
+
XML:=TXMLDocument.Create;
try
SaveToXML(AProject,XML);
@@ -464,5 +498,263 @@
end;
end;
+{$IFDEF DoDi}
+
+constructor TXMLFPDocOptions.Create(AOwner: TComponent; const AFile: string);
+begin
+ inherited Create(AOwner);
+ FileName := AFile;
+end;
+
+function TXMLFPDocOptions.LoadProject(AProject: TFPDocProject): boolean;
+begin
+ Result := False;
+ if FileName = '' then
+ exit; //message?
+ Modified := not FileExists(FileName);
+ if Modified then begin
+ WriteLn('Not found: ', GetCurrentDir, DirectorySeparator, FileName);
+ exit; //save later
+ end;
+ try
+ LoadOptionsFromFile(AProject, FileName);
+ Result := True;
+ except
+ //message?
+ end;
+end;
+
+{$IFDEF DoDiSave}
+
+function TXMLFPDocOptions.OldSaveToXML(AProject: TFPDocProject; ADoc:
TXMLDocument): boolean;
+var
+ i: integer;
+ E: TDOMElement;
+begin
+ doc := ADoc;
+ E := NeedNode(doc.FirstChild, 'packages');
+ for i := 0 to AProject.Packages.Count - 1 do
+ SavePackage(AProject.Packages[i], E);
+ SaveOptions(AProject);
+ Result := True;
+end;
+
+function TXMLFPDocOptions.SaveOptions(AProject: TFPDocProject): boolean;
+var
+ i: integer;
+ Opts: TDOMElement;
+ O: TEngineOptions;
+ n: string;
+
+ procedure AddBool(const n: string; v: Boolean; reverse: boolean=False);
+ var
+ Nn: TDOMNode;
+ Opt: TDOMElement;
+ begin
+ if reverse = v then
+ exit; //only store True options
+ Nn := FindChild(Opts, 'name', n);
+ if Nn <> nil then
+ exit; //already stored
+ Opt := doc.CreateElement('option');
+ Opts.AppendChild(Opt);
+ Opt['name'] := n;
+ Opt['value'] := 'true';
+ end;
+
+ procedure AddStr(const n, v: string);
+ var
+ Nn: TDOMNode;
+ Opt: TDOMElement;
+ begin
+ if v = '' then
+ exit; //nostore empty options
+ Nn := FindChild(Opts, 'name', n);
+ if Nn <> nil then
+ exit; //already stored
+ Opt := doc.CreateElement('option');
+ Opts.AppendChild(Opt);
+ Opt['name'] := n;
+ Opt['value'] := v;
+ end;
+
+begin
+ Opts := doc.FirstChild.FindNode('options') as TDOMElement;
+ O := AProject.Options;
+ AddBool('hide-protected', O.HideProtected); //todo: use option name constants
+ AddBool('warn-no-node', O.WarnNoNode);
+ AddBool('show-private', O.ShowPrivate);
+ AddBool('stop-on-parser-error', O.StopOnParseError);
+ AddStr('ostarget', O.OSTarget);
+ AddStr('cputarget', O.CPUTarget);
+ AddStr('mo-dir', O.MoDir);
+ AddBool('parse-impl', O.InterfaceOnly, True);
+ AddStr('format', O.Backend);
+ AddStr('language', O.Language);
+ AddStr('package', O.DefaultPackageName);
+ AddBool('dont-trim', O.DontTrim);
+ Result := True;
+end;
+
+function TXMLFPDocOptions.SavePackage(APackage: TFPDocPackage; Pkgs:
TDOMElement
+ ): boolean;
+var
+ i: integer;
+ pkg, U, M, D: TDOMElement;
+ Mn: TDOMNode;
+ fo, f, o: string; //split filename+options
+
+ procedure SplitUnit(s: string);
+ var
+ i: integer;
+ begin
+ //problem separating options from unit :-(
+ f := s;
+ o := '';
+ end;
+
+begin
+//create package element
+ pkg := doc.CreateElement('package');
+ Pkgs.AppendChild(pkg);
+//add attribs - if not empty
+ pkg['name'] := APackage.Name;
+ pkg['output']:=APackage.Output;
+ pkg['content']:=APackage.ContentFile;
+ SaveImports(APackage, pkg);
+//add units
+ U := NeedNode(pkg, 'units');
+ for i := 0 to APackage.Inputs.Count - 1 do begin
+ fo := APackage.Inputs[i];
+ SplitUnit(fo);
+ Mn := FindChild(U, 'file', f);
+ if Mn=nil then begin
+ M := doc.CreateElement('unit');
+ U.AppendChild(M);
+ M['file'] := f;
+ if o<>'' then
+ M['options'] := o;
+ end; //else update?
+ end;
+//add descriptions
+ D := NeedNode(pkg, 'descriptions');
+ for i := 0 to APackage.Descriptions.Count - 1 do begin
+ f := APackage.Descriptions[i];
+ Mn :=FindChild(D, 'file', f);
+ if Mn=nil then begin
+ U := doc.CreateElement('description');
+ D.AppendChild(U);
+ U['file'] := f;
+ end;
+ end;
+ Result := True;
+end;
+
+procedure TXMLFPDocOptions.SaveImports(APackage: TFPDocPackage; Pkg:
TDOMElement
+ );
+var
+ i, n: integer;
+ s: string;
+ U: TDOMElement;
+begin
+//save all package options (currently: imports only)
+ n := APackage.Imports.Count;
+//add imports
+ for i := 0 to n-1 do begin
+ U := NeedNode(pkg, 'import');
+ s := APackage.Imports.Names[i];
+ U['package'] := s;
+ end;
+//more options?
+end;
+
+function TXMLFPDocOptions.NeedNode(P: TDomNode; const ATag: string
+ ): TDOMElement;
+var
+ n: TDOMNode absolute result;
+begin
+ n := p.FindNode(ATag);
+ if assigned(n) then
+ exit;
+//create new child
+ Result := doc.CreateElement(ATag);
+ p.AppendChild(Result);
+end;
+
+function TXMLFPDocOptions.FindChild(N: TDOMNode; const ATag, AVal: string
+ ): TDOMNode;
+begin
+ Result := N.FirstChild;
+ while assigned(Result) do begin
+ if (Result is TDOMElement)
+ and (CompareText(TDOMElement(Result).AttribStrings[ATag], AVal)=0) then
+ exit;
+ Result := Result.NextSibling;
+ end;
+end;
+
+procedure TXMLFPDocOptions.LoadOptionsFromFile(AProject: TFPDocProject;
+ const AFileName: String);
+
+Var
+ XML : TXMLDocument;
+
+begin
+ XMLRead.ReadXMLFile(XML,AFileName);
+ try
+ LoadFromXML(AProject,XML);
+ finally
+ FreeAndNil(XML);
+ end;
+end;
+
+function TXMLFPDocOptions.SaveProject(AProject: TFPDocProject): boolean;
+Var
+ ss: TStringStream;
+ XML : TXMLDocument;
+const
+ template = '<docproject>'+LineEnding+
+ ' <packages>'+LineEnding+
+ ' </packages>'+LineEnding+
+ ' <options>'+LineEnding+
+ ' </options>'+LineEnding+
+ '</docproject>'+LineEnding;
+begin
+ Result := False; //in case of exceptions
+ if not Modified then
+ exit; //don't save unmodified project
+ if FileName = '' then
+ exit; //no filename?
+//debug
+ if dGlobals.Verbose then
+ WriteLn('Creating FPDoc project ', GetCurrentDir, DirectorySeparator,
FileName);
+ try
+ ss := TStringStream.Create(template);
+ XMLRead.ReadXMLFile(XML, ss);
+ try
+ Result := OldSaveToXML(AProject,XML);
+ if Result then begin
+ XMLWrite.WriteXMLFile(XML, FileName);
+ Modified := False;
+ end;
+ finally
+ XML.Free;
+ ss.Free;
+ end;
+ except
+ //message?
+ end;
+end;
+
+{$ELSE}
+function TXMLFPDocOptions.SaveProject(AProject: TFPDocProject): boolean;
+begin
+ Result := False;
+ SaveOptionsToFile(AProject, FileName);
+ Result := True;
+end;
+
+{$ENDIF DoDiSave}
+{$ENDIF DoDi}
end.
_______________________________________________
fpc-devel maillist - fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel